diff --git a/.gitignore b/.gitignore index a2d6ca9c3..27eeb9c22 100644 --- a/.gitignore +++ b/.gitignore @@ -111,5 +111,7 @@ conformance/conformance.pb.h conformance/Conformance.pbobjc.h conformance/Conformance.pbobjc.m conformance/conformance.rb +conformance/google/ conformance/javac_middleman +conformance/lite/ conformance/protoc_middleman diff --git a/.travis.yml b/.travis.yml index 9bc4d88ff..bcf3851bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ os: # The Objective C build needs Xcode 7.0 or later. osx_image: xcode7.2 script: - - ./travis.sh $CONFIG + - ./tests.sh $CONFIG env: - CONFIG=cpp - CONFIG=cpp_distcheck @@ -71,5 +71,14 @@ matrix: # we moved to an OS X image that is 10.11. - os: osx env: CONFIG=python_cpp + # xctool 0.2.8 seems to have a bug where it randomly kills tests saying + # they failed. + # https://github.com/facebook/xctool/issues/619 + # https://github.com/google/protobuf/issues/1232 + # travis updated their images to include 0.2.8: + # https://blog.travis-ci.com/2016-03-23-xcode-image-updates + # Mark the iOS test as flakey so these failures don't turn things red. + - os: osx + env: CONFIG=objectivec_ios notifications: email: false diff --git a/BUILD b/BUILD index 9cbddd5bf..8b1046b90 100644 --- a/BUILD +++ b/BUILD @@ -15,16 +15,58 @@ COPTS = [ "-Wno-error=unused-function", ] -# Bazel should provide portable link_opts for pthread. -LINK_OPTS = ["-lpthread"] +config_setting( + name = "android", + values = { + "crosstool_top": "//external:android/crosstool", + }, +) + +# Android builds do not need to link in a separate pthread library. +LINK_OPTS = select({ + ":android": [], + "//conditions:default": ["-lpthread"], +}) load( "protobuf", "cc_proto_library", "py_proto_library", + "internal_gen_well_known_protos_java", "internal_protobuf_py_tests", ) +config_setting( + name = "ios_armv7", + values = { + "ios_cpu": "armv7", + }, +) + +config_setting( + name = "ios_armv7s", + values = { + "ios_cpu": "armv7s", + }, +) + +config_setting( + name = "ios_arm64", + values = { + "ios_cpu": "arm64", + }, +) + +IOS_ARM_COPTS = COPTS + [ + "-DOS_IOS", + "-miphoneos-version-min=7.0", + "-arch armv7", + "-arch armv7s", + "-arch arm64", + "-D__thread=", + "-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.2.sdk/", +] + cc_library( name = "protobuf_lite", srcs = [ @@ -54,7 +96,12 @@ cc_library( "src/google/protobuf/wire_format_lite.cc", ], hdrs = glob(["src/google/protobuf/**/*.h"]), - copts = COPTS, + copts = select({ + ":ios_armv7": IOS_ARM_COPTS, + ":ios_armv7s": IOS_ARM_COPTS, + ":ios_arm64": IOS_ARM_COPTS, + "//conditions:default": COPTS, + }), includes = ["src/"], linkopts = LINK_OPTS, visibility = ["//visibility:public"], @@ -119,7 +166,12 @@ cc_library( "src/google/protobuf/wrappers.pb.cc", ], hdrs = glob(["src/**/*.h"]), - copts = COPTS, + copts = select({ + ":ios_armv7": IOS_ARM_COPTS, + ":ios_armv7s": IOS_ARM_COPTS, + ":ios_arm64": IOS_ARM_COPTS, + "//conditions:default": COPTS, + }), includes = ["src/"], linkopts = LINK_OPTS, visibility = ["//visibility:public"], @@ -152,6 +204,12 @@ RELATIVE_WELL_KNOWN_PROTOS = [ WELL_KNOWN_PROTOS = ["src/" + s for s in RELATIVE_WELL_KNOWN_PROTOS] +filegroup( + name = "well_known_protos", + srcs = WELL_KNOWN_PROTOS, + visibility = ["//visibility:public"], +) + cc_proto_library( name = "cc_wkt_protos", srcs = WELL_KNOWN_PROTOS, @@ -208,6 +266,7 @@ cc_library( "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", @@ -318,6 +377,8 @@ RELATIVE_TEST_PROTOS = [ "google/protobuf/unittest_preserve_unknown_enum.proto", "google/protobuf/unittest_preserve_unknown_enum2.proto", "google/protobuf/unittest_proto3_arena.proto", + "google/protobuf/unittest_proto3_arena_lite.proto", + "google/protobuf/unittest_proto3_lite.proto", "google/protobuf/unittest_well_known_types.proto", "google/protobuf/util/internal/testdata/anys.proto", "google/protobuf/util/internal/testdata/books.proto", @@ -378,6 +439,7 @@ cc_test( "src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc", "src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc", "src/google/protobuf/compiler/cpp/cpp_unittest.cc", + "src/google/protobuf/compiler/cpp/metadata_test.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", @@ -402,7 +464,9 @@ cc_test( "src/google/protobuf/message_unittest.cc", "src/google/protobuf/no_field_presence_test.cc", "src/google/protobuf/preserve_unknown_enum_test.cc", + "src/google/protobuf/proto3_arena_lite_unittest.cc", "src/google/protobuf/proto3_arena_unittest.cc", + "src/google/protobuf/proto3_lite_unittest.cc", "src/google/protobuf/reflection_ops_unittest.cc", "src/google/protobuf/repeated_field_reflection_unittest.cc", "src/google/protobuf/repeated_field_unittest.cc", @@ -457,16 +521,8 @@ cc_test( ################################################################################ # Java support ################################################################################ -genrule( - name = "gen_well_known_protos_java", +internal_gen_well_known_protos_java( srcs = WELL_KNOWN_PROTOS, - outs = [ - "wellknown.srcjar", - ], - cmd = "$(location :protoc) --java_out=$(@D)/wellknown.jar" + - " -Isrc $(SRCS) " + - " && mv $(@D)/wellknown.jar $(@D)/wellknown.srcjar", - tools = [":protoc"], ) java_library( @@ -479,6 +535,19 @@ java_library( visibility = ["//visibility:public"], ) +java_library( + name = "protobuf_java_util", + srcs = glob([ + "java/util/src/main/java/com/google/protobuf/util/*.java", + ]), + deps = [ + "protobuf_java", + "//external:gson", + "//external:guava", + ], + visibility = ["//visibility:public"], +) + ################################################################################ # Python support ################################################################################ @@ -495,6 +564,7 @@ py_library( "python/google/protobuf/internal/test_util.py", ], ), + srcs_version = "PY2AND3", imports = ["python"], ) @@ -579,6 +649,7 @@ py_proto_library( include = "src", default_runtime = "", protoc = ":protoc", + srcs_version = "PY2AND3", deps = [":protobuf_python"], ) @@ -591,6 +662,7 @@ py_proto_library( include = "python", default_runtime = ":protobuf_python", protoc = ":protoc", + srcs_version = "PY2AND3", deps = [":python_common_test_protos"], ) diff --git a/Makefile.am b/Makefile.am index 064efdfb1..3e9888166 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,7 +9,7 @@ AUTOMAKE_OPTIONS = foreign SUBDIRS = . src # Always include gmock in distributions. -DIST_SUBDIRS = $(subdirs) src conformance +DIST_SUBDIRS = $(subdirs) src conformance benchmarks # Build gmock before we build protobuf tests. We don't add gmock to SUBDIRS # because then "make check" would also build and run all of gmock's own tests, @@ -36,6 +36,10 @@ clean-local: echo "Making clean in conformance"; \ cd conformance && $(MAKE) $(AM_MAKEFLAGS) clean; \ fi; \ + if test -e benchmarks/Makefile; then \ + echo "Making clean in benchmarks"; \ + cd benchmarks && $(MAKE) $(AM_MAKEFLAGS) clean; \ + fi; \ if test -e objectivec/DevTools; then \ echo "Cleaning any ObjC pyc files"; \ rm -f objectivec/DevTools/*.pyc; \ @@ -154,6 +158,7 @@ csharp_EXTRA_DIST= \ csharp/src/Google.Protobuf/Reflection/MethodDescriptor.cs \ csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs \ csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs \ + csharp/src/Google.Protobuf/Reflection/OriginalNameAttribute.cs \ csharp/src/Google.Protobuf/Reflection/PackageDescriptor.cs \ csharp/src/Google.Protobuf/Reflection/PartialClasses.cs \ csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs \ @@ -193,6 +198,8 @@ java_EXTRA_DIST= java/core/src/main/java/com/google/protobuf/BlockingRpcChannel.java \ java/core/src/main/java/com/google/protobuf/BlockingService.java \ java/core/src/main/java/com/google/protobuf/BooleanArrayList.java \ + java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java \ + java/core/src/main/java/com/google/protobuf/ByteOutput.java \ java/core/src/main/java/com/google/protobuf/ByteString.java \ java/core/src/main/java/com/google/protobuf/CodedInputStream.java \ java/core/src/main/java/com/google/protobuf/CodedOutputStream.java \ @@ -243,6 +250,8 @@ java_EXTRA_DIST= java/core/src/main/java/com/google/protobuf/SmallSortedMap.java \ java/core/src/main/java/com/google/protobuf/TextFormat.java \ java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java \ + java/core/src/main/java/com/google/protobuf/TextFormatParseInfoTree.java \ + java/core/src/main/java/com/google/protobuf/TextFormatParseLocation.java \ java/core/src/main/java/com/google/protobuf/UninitializedMessageException.java \ java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java \ java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java \ @@ -254,6 +263,7 @@ java_EXTRA_DIST= java/core/src/test/java/com/google/protobuf/AnyTest.java \ java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java \ java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java \ + java/core/src/test/java/com/google/protobuf/ByteBufferWriterTest.java \ java/core/src/test/java/com/google/protobuf/ByteStringTest.java \ java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java \ java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java \ @@ -262,6 +272,7 @@ java_EXTRA_DIST= java/core/src/test/java/com/google/protobuf/DescriptorsTest.java \ java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java \ java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java \ + java/core/src/test/java/com/google/protobuf/EnumTest.java \ java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java \ java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java \ java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java \ @@ -294,6 +305,8 @@ java_EXTRA_DIST= java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java \ java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java \ java/core/src/test/java/com/google/protobuf/TestUtil.java \ + java/core/src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java \ + java/core/src/test/java/com/google/protobuf/TextFormatParseLocationTest.java \ java/core/src/test/java/com/google/protobuf/TextFormatTest.java \ java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java \ java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java \ @@ -381,13 +394,11 @@ objectivec_EXTRA_DIST= \ objectivec/DevTools/full_mac_build.sh \ objectivec/DevTools/pddm.py \ objectivec/DevTools/pddm_tests.py \ - objectivec/generate_descriptors_proto.sh \ + objectivec/generate_well_known_types.sh \ objectivec/google/protobuf/Any.pbobjc.h \ objectivec/google/protobuf/Any.pbobjc.m \ objectivec/google/protobuf/Api.pbobjc.h \ objectivec/google/protobuf/Api.pbobjc.m \ - objectivec/google/protobuf/Descriptor.pbobjc.h \ - objectivec/google/protobuf/Descriptor.pbobjc.m \ objectivec/google/protobuf/Duration.pbobjc.h \ objectivec/google/protobuf/Duration.pbobjc.m \ objectivec/google/protobuf/Empty.pbobjc.h \ diff --git a/Protobuf.podspec b/Protobuf.podspec index 02c83723c..0bbd06df0 100644 --- a/Protobuf.podspec +++ b/Protobuf.podspec @@ -17,7 +17,6 @@ Pod::Spec.new do |s| s.source_files = 'objectivec/*.{h,m}', 'objectivec/google/protobuf/Any.pbobjc.{h,m}', 'objectivec/google/protobuf/Api.pbobjc.{h,m}', - 'objectivec/google/protobuf/Descriptor.pbobjc.{h,m}', 'objectivec/google/protobuf/Duration.pbobjc.h', 'objectivec/google/protobuf/Empty.pbobjc.{h,m}', 'objectivec/google/protobuf/FieldMask.pbobjc.{h,m}', diff --git a/WORKSPACE b/WORKSPACE index 1e8e0a7f5..065dc8198 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -31,3 +31,23 @@ bind( name = "six", actual = "@six_archive//:six", ) + +maven_jar( + name = "guava_maven", + artifact = "com.google.guava:guava:18.0", +) + +bind( + name = "guava", + actual = "@guava_maven//jar", +) + +maven_jar( + name = "gson_maven", + artifact = "com.google.code.gson:gson:2.3", +) + +bind( + name = "gson", + actual = "@gson_maven//jar", +) diff --git a/benchmarks/Makefile.am b/benchmarks/Makefile.am new file mode 100644 index 000000000..f730afe57 --- /dev/null +++ b/benchmarks/Makefile.am @@ -0,0 +1,69 @@ + +benchmarks_protoc_inputs = \ + benchmarks.proto \ + benchmark_messages_proto3.proto + +benchmarks_protoc_inputs_proto2 = \ + benchmark_messages_proto2.proto + +benchmarks_protoc_outputs = \ + benchmarks.pb.cc \ + benchmarks.pb.h \ + benchmark_messages_proto3.pb.cc \ + benchmark_messages_proto3.pb.h + +benchmarks_protoc_outputs_proto2 = \ + benchmark_messages_proto2.pb.cc \ + benchmark_messages_proto2.pb.h + +bin_PROGRAMS = generate-datasets + +generate_datasets_LDADD = $(top_srcdir)/src/libprotobuf.la +generate_datasets_SOURCES = generate_datasets.cc +generate_datasets_CPPFLAGS = -I$(top_srcdir)/src -I$(srcdir) +nodist_generate_datasets_SOURCES = \ + $(benchmarks_protoc_outputs) \ + $(benchmarks_protoc_outputs_proto2) + +# Explicit deps because BUILT_SOURCES are only done before a "make all/check" +# so a direct "make test_cpp" could fail if parallel enough. +# See: https://www.gnu.org/software/automake/manual/html_node/Built-Sources-Example.html#Recording-Dependencies-manually +generate_datasets-generate_datasets.$(OBJEXT): benchmarks.pb.h + +$(benchmarks_protoc_outputs): protoc_middleman +$(benchmarks_protoc_outputs_proto2): protoc_middleman2 + +CLEANFILES = \ + $(benchmarks_protoc_outputs) \ + $(benchmarks_protoc_outputs_proto2) \ + protoc_middleman \ + protoc_middleman2 \ + dataset.* + +MAINTAINERCLEANFILES = \ + Makefile.in + +if USE_EXTERNAL_PROTOC + +protoc_middleman: $(benchmarks_protoc_inputs) + $(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. $(benchmarks_protoc_inputs) + touch protoc_middleman + +protoc_middleman2: $(benchmarks_protoc_inputs_proto2) + $(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. $(benchmarks_protoc_inputs_proto2) + touch protoc_middleman2 + +else + +# We have to cd to $(srcdir) before executing protoc because $(protoc_inputs) is +# relative to srcdir, which may not be the same as the current directory when +# building out-of-tree. +protoc_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(benchmarks_protoc_inputs) $(well_known_type_protoc_inputs) + oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd $(benchmarks_protoc_inputs) ) + touch protoc_middleman + +protoc_middleman2: $(top_srcdir)/src/protoc$(EXEEXT) $(benchmarks_protoc_inputs_proto2) $(well_known_type_protoc_inputs) + oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd $(benchmarks_protoc_inputs_proto2) ) + touch protoc_middleman + +endif diff --git a/benchmarks/README.md b/benchmarks/README.md new file mode 100644 index 000000000..c90278058 --- /dev/null +++ b/benchmarks/README.md @@ -0,0 +1,28 @@ + +# Protocol Buffers Benchmarks + +This directory contains benchmarking schemas and data sets that you +can use to test a variety of performance scenarios against your +protobuf language runtime. + +The schema for the datasets is described in `benchmarks.proto`. + +Generate the data sets like so: + +``` +$ make +$ ./generate-datasets +Wrote dataset: dataset.google_message1_proto3.pb +Wrote dataset: dataset.google_message1_proto2.pb +Wrote dataset: dataset.google_message2.pb +$ +``` + +Each data set will be written to its own file. Benchmarks will +likely want to run several benchmarks against each data set (parse, +serialize, possibly JSON, possibly using different APIs, etc). + +We would like to add more data sets. In general we will favor data sets +that make the overall suite diverse without being too large or having +too many similar tests. Ideally everyone can run through the entire +suite without the test run getting too long. diff --git a/benchmarks/google_speed.proto b/benchmarks/benchmark_messages_proto2.proto similarity index 91% rename from benchmarks/google_speed.proto rename to benchmarks/benchmark_messages_proto2.proto index 16f6d6780..01f67a1af 100644 --- a/benchmarks/google_speed.proto +++ b/benchmarks/benchmark_messages_proto2.proto @@ -1,11 +1,14 @@ +// Benchmark messages for proto2. + syntax = "proto2"; -package benchmarks; +package benchmarks.proto2; +option java_package = "com.google.protobuf.benchmarks"; -option java_outer_classname = "GoogleSpeed"; +// This is the default, but we specify it here explicitly. option optimize_for = SPEED; -message SpeedMessage1 { +message GoogleMessage1 { required string field1 = 1; optional string field9 = 9; optional string field18 = 18; @@ -40,7 +43,7 @@ message SpeedMessage1 { optional int32 field23 = 23 [default=0]; optional bool field24 = 24 [default=false]; optional int32 field25 = 25 [default=0]; - optional SpeedMessage1SubMessage field15 = 15; + optional GoogleMessage1SubMessage field15 = 15; optional bool field78 = 78; optional int32 field67 = 67 [default=0]; optional int32 field68 = 68; @@ -49,7 +52,7 @@ message SpeedMessage1 { optional int32 field131 = 131 [default=0]; } -message SpeedMessage1SubMessage { +message GoogleMessage1SubMessage { optional int32 field1 = 1 [default=0]; optional int32 field2 = 2 [default=0]; optional int32 field3 = 3 [default=0]; @@ -72,7 +75,7 @@ message SpeedMessage1SubMessage { optional uint64 field300 = 300; } -message SpeedMessage2 { +message GoogleMessage2 { optional string field1 = 1; optional int64 field3 = 3; optional int64 field4 = 4; @@ -112,7 +115,7 @@ message SpeedMessage2 { repeated int32 field73 = 73; optional int32 field20 = 20 [default=0]; optional string field24 = 24; - optional SpeedMessage2GroupedMessage field31 = 31; + optional GoogleMessage2GroupedMessage field31 = 31; } repeated string field128 = 128; optional int64 field131 = 131; @@ -123,7 +126,7 @@ message SpeedMessage2 { optional bool field206 = 206 [default=false]; } -message SpeedMessage2GroupedMessage { +message GoogleMessage2GroupedMessage { optional float field1 = 1; optional float field2 = 2; optional float field3 = 3 [default=0.0]; diff --git a/benchmarks/benchmark_messages_proto3.proto b/benchmarks/benchmark_messages_proto3.proto new file mode 100644 index 000000000..32f586986 --- /dev/null +++ b/benchmarks/benchmark_messages_proto3.proto @@ -0,0 +1,76 @@ +// Benchmark messages for proto3. + +syntax = "proto3"; + +package benchmarks.proto3; +option java_package = "com.google.protobuf.benchmarks"; + +// This is the default, but we specify it here explicitly. +option optimize_for = SPEED; + +message GoogleMessage1 { + string field1 = 1; + string field9 = 9; + string field18 = 18; + bool field80 = 80; + bool field81 = 81; + int32 field2 = 2; + int32 field3 = 3; + int32 field280 = 280; + int32 field6 = 6; + int64 field22 = 22; + string field4 = 4; + repeated fixed64 field5 = 5; + bool field59 = 59; + string field7 = 7; + int32 field16 = 16; + int32 field130 = 130; + bool field12 = 12; + bool field17 = 17; + bool field13 = 13; + bool field14 = 14; + int32 field104 = 104; + int32 field100 = 100; + int32 field101 = 101; + string field102 = 102; + string field103 = 103; + int32 field29 = 29; + bool field30 = 30; + int32 field60 = 60; + int32 field271 = 271; + int32 field272 = 272; + int32 field150 = 150; + int32 field23 = 23; + bool field24 = 24; + int32 field25 = 25; + GoogleMessage1SubMessage field15 = 15; + bool field78 = 78; + int32 field67 = 67; + int32 field68 = 68; + int32 field128 = 128; + string field129 = 129; + int32 field131 = 131; +} + +message GoogleMessage1SubMessage { + int32 field1 = 1; + int32 field2 = 2; + int32 field3 = 3; + string field15 = 15; + bool field12 = 12; + int64 field13 = 13; + int64 field14 = 14; + int32 field16 = 16; + int32 field19 = 19; + bool field20 = 20; + bool field28 = 28; + fixed64 field21 = 21; + int32 field22 = 22; + bool field23 = 23; + bool field206 = 206; + fixed32 field203 = 203; + int32 field204 = 204; + string field205 = 205; + uint64 field207 = 207; + uint64 field300 = 300; +} diff --git a/benchmarks/benchmarks.proto b/benchmarks/benchmarks.proto new file mode 100644 index 000000000..51c0b5487 --- /dev/null +++ b/benchmarks/benchmarks.proto @@ -0,0 +1,63 @@ +// 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. + +syntax = "proto3"; +package benchmarks; +option java_package = "com.google.protobuf.benchmarks"; + +message BenchmarkDataset { + // Name of the benchmark dataset. This should be unique across all datasets. + // Should only contain word characters: [a-zA-Z0-9_] + string name = 1; + + // Fully-qualified name of the protobuf message for this dataset. + // It will be one of the messages defined benchmark_messages_proto2.proto + // or benchmark_messages_proto3.proto. + // + // Implementations that do not support reflection can implement this with + // an explicit "if/else" chain that lists every known message defined + // in those files. + string message_name = 2; + + // The payload(s) for this dataset. They should be parsed or serialized + // in sequence, in a loop, ie. + // + // while (!benchmarkDone) { // Benchmark runner decides when to exit. + // for (i = 0; i < benchmark.payload.length; i++) { + // parse(benchmark.payload[i]) + // } + // } + // + // This is intended to let datasets include a variety of data to provide + // potentially more realistic results than just parsing the same message + // over and over. A single message parsed repeatedly could yield unusually + // good branch prediction performance. + repeated bytes payload = 3; +} diff --git a/benchmarks/generate_datasets.cc b/benchmarks/generate_datasets.cc new file mode 100644 index 000000000..61e7adf1b --- /dev/null +++ b/benchmarks/generate_datasets.cc @@ -0,0 +1,117 @@ +// 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 +#include +#include "benchmarks.pb.h" + +using benchmarks::BenchmarkDataset; +using google::protobuf::Descriptor; +using google::protobuf::DescriptorPool; +using google::protobuf::Message; +using google::protobuf::MessageFactory; + +std::set names; + +const char *file_prefix = "dataset."; +const char *file_suffix = ".pb"; + +void WriteFileWithPayloads(const std::string& name, + const std::string& message_name, + const std::vector& payload) { + if (!names.insert(name).second) { + std::cerr << "Duplicate test name: " << name << "\n"; + abort(); + } + + // First verify that this message name exists in our set of benchmark messages + // and that these payloads are valid for the given message. + const Descriptor* d = + DescriptorPool::generated_pool()->FindMessageTypeByName(message_name); + + if (!d) { + std::cerr << "For dataset " << name << ", no such message: " + << message_name << "\n"; + abort(); + } + + Message* m = MessageFactory::generated_factory()->GetPrototype(d)->New(); + + for (size_t i = 0; i < payload.size(); i++) { + if (!m->ParseFromString(payload[i])) { + std::cerr << "For dataset " << name << ", payload[" << i << "] fails " + << "to parse\n"; + abort(); + } + } + + BenchmarkDataset dataset; + dataset.set_name(name); + dataset.set_message_name(message_name); + for (size_t i = 0; i < payload.size(); i++) { + dataset.add_payload()->assign(payload[i]); + } + + std::ofstream writer; + std::string fname = file_prefix + name + file_suffix; + writer.open(fname.c_str()); + dataset.SerializeToOstream(&writer); + writer.close(); + + std::cerr << "Wrote dataset: " << fname << "\n"; +} + +void WriteFile(const std::string& name, const std::string& message_name, + const std::string& payload) { + std::vector payloads; + payloads.push_back(payload); + WriteFileWithPayloads(name, message_name, payloads); +} + +std::string ReadFile(const std::string& name) { + std::ifstream file(name.c_str()); + GOOGLE_CHECK(file.is_open()) << "Couldn't find file '" << name << + "', please make sure you are running " + "this command from the benchmarks/ " + "directory.\n"; + return std::string((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); +} + +int main() { + WriteFile("google_message1_proto3", "benchmarks.proto3.GoogleMessage1", + ReadFile("google_message1.dat")); + WriteFile("google_message1_proto2", "benchmarks.proto2.GoogleMessage1", + ReadFile("google_message1.dat")); + + // Not in proto3 because it has a group, which is not supported. + WriteFile("google_message2", "benchmarks.proto2.GoogleMessage2", + ReadFile("google_message2.dat")); +} diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 15ae457aa..f32a0e4e6 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -118,6 +118,8 @@ if (MSVC) # Build with multiple processes add_definitions(/MP) add_definitions(/wd4244 /wd4267 /wd4018 /wd4355 /wd4800 /wd4251 /wd4996 /wd4146 /wd4305) + # Allow big object + add_definitions(/bigobj) string(REPLACE "/" "\\" PROTOBUF_SOURCE_WIN32_PATH ${protobuf_SOURCE_DIR}) string(REPLACE "/" "\\" PROTOBUF_BINARY_WIN32_PATH ${protobuf_BINARY_DIR}) configure_file(extract_includes.bat.in extract_includes.bat) diff --git a/cmake/tests.cmake b/cmake/tests.cmake index 941f33d81..76fdf8efd 100644 --- a/cmake/tests.cmake +++ b/cmake/tests.cmake @@ -52,6 +52,8 @@ set(tests_protos google/protobuf/unittest_preserve_unknown_enum.proto google/protobuf/unittest_preserve_unknown_enum2.proto google/protobuf/unittest_proto3_arena.proto + google/protobuf/unittest_proto3_arena_lite.proto + google/protobuf/unittest_proto3_lite.proto google/protobuf/unittest_well_known_types.proto google/protobuf/util/internal/testdata/anys.proto google/protobuf/util/internal/testdata/books.proto @@ -116,6 +118,7 @@ set(tests_files ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/cpp_unittest.cc + ${protobuf_source_dir}/src/google/protobuf/compiler/cpp/metadata_test.cc ${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc ${protobuf_source_dir}/src/google/protobuf/compiler/importer_unittest.cc ${protobuf_source_dir}/src/google/protobuf/compiler/java/java_doc_comment_unittest.cc @@ -140,7 +143,9 @@ set(tests_files ${protobuf_source_dir}/src/google/protobuf/message_unittest.cc ${protobuf_source_dir}/src/google/protobuf/no_field_presence_test.cc ${protobuf_source_dir}/src/google/protobuf/preserve_unknown_enum_test.cc + ${protobuf_source_dir}/src/google/protobuf/proto3_arena_lite_unittest.cc ${protobuf_source_dir}/src/google/protobuf/proto3_arena_unittest.cc + ${protobuf_source_dir}/src/google/protobuf/proto3_lite_unittest.cc ${protobuf_source_dir}/src/google/protobuf/reflection_ops_unittest.cc ${protobuf_source_dir}/src/google/protobuf/repeated_field_reflection_unittest.cc ${protobuf_source_dir}/src/google/protobuf/repeated_field_unittest.cc diff --git a/configure.ac b/configure.ac index 33a6c64d6..d56a70470 100644 --- a/configure.ac +++ b/configure.ac @@ -180,5 +180,5 @@ export CFLAGS export CXXFLAGS AC_CONFIG_SUBDIRS([gmock]) -AC_CONFIG_FILES([Makefile src/Makefile conformance/Makefile protobuf.pc protobuf-lite.pc]) +AC_CONFIG_FILES([Makefile src/Makefile benchmarks/Makefile conformance/Makefile protobuf.pc protobuf-lite.pc]) AC_OUTPUT diff --git a/conformance/ConformanceJava.java b/conformance/ConformanceJava.java index a983ba3c8..43787ffcb 100644 --- a/conformance/ConformanceJava.java +++ b/conformance/ConformanceJava.java @@ -129,7 +129,7 @@ class ConformanceJava { typeRegistry = TypeRegistry.newBuilder().add( Conformance.TestAllTypes.getDescriptor()).build(); while (doTestIo()) { - // Empty. + this.testCount++; } System.err.println("ConformanceJava: received EOF from test runner after " + diff --git a/conformance/ConformanceJavaLite.java b/conformance/ConformanceJavaLite.java new file mode 100644 index 000000000..121dc7d1b --- /dev/null +++ b/conformance/ConformanceJavaLite.java @@ -0,0 +1,125 @@ + +import com.google.protobuf.conformance.Conformance; +import com.google.protobuf.InvalidProtocolBufferException; + +class ConformanceJavaLite { + private int testCount = 0; + + private boolean readFromStdin(byte[] buf, int len) throws Exception { + int ofs = 0; + while (len > 0) { + int read = System.in.read(buf, ofs, len); + if (read == -1) { + return false; // EOF + } + ofs += read; + len -= read; + } + + return true; + } + + private void writeToStdout(byte[] buf) throws Exception { + System.out.write(buf); + } + + // Returns -1 on EOF (the actual values will always be positive). + private int readLittleEndianIntFromStdin() throws Exception { + byte[] buf = new byte[4]; + if (!readFromStdin(buf, 4)) { + return -1; + } + return (buf[0] & 0xff) + | ((buf[1] & 0xff) << 8) + | ((buf[2] & 0xff) << 16) + | ((buf[3] & 0xff) << 24); + } + + private void writeLittleEndianIntToStdout(int val) throws Exception { + byte[] buf = new byte[4]; + buf[0] = (byte)val; + buf[1] = (byte)(val >> 8); + buf[2] = (byte)(val >> 16); + buf[3] = (byte)(val >> 24); + writeToStdout(buf); + } + + private Conformance.ConformanceResponse doTest(Conformance.ConformanceRequest request) { + Conformance.TestAllTypes testMessage; + + switch (request.getPayloadCase()) { + case PROTOBUF_PAYLOAD: { + try { + testMessage = Conformance.TestAllTypes.parseFrom(request.getProtobufPayload()); + } catch (InvalidProtocolBufferException e) { + return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build(); + } + break; + } + case JSON_PAYLOAD: { + return Conformance.ConformanceResponse.newBuilder().setSkipped( + "Lite runtime does not suport Json Formant.").build(); + } + case PAYLOAD_NOT_SET: { + throw new RuntimeException("Request didn't have payload."); + } + + default: { + throw new RuntimeException("Unexpected payload case."); + } + } + + switch (request.getRequestedOutputFormat()) { + case UNSPECIFIED: + throw new RuntimeException("Unspecified output format."); + + case PROTOBUF: + return Conformance.ConformanceResponse.newBuilder().setProtobufPayload(testMessage.toByteString()).build(); + + case JSON: + return Conformance.ConformanceResponse.newBuilder().setSkipped( + "Lite runtime does not suport Json Formant.").build(); + + default: { + throw new RuntimeException("Unexpected request output."); + } + } + } + + private boolean doTestIo() throws Exception { + int bytes = readLittleEndianIntFromStdin(); + + if (bytes == -1) { + return false; // EOF + } + + byte[] serializedInput = new byte[bytes]; + + if (!readFromStdin(serializedInput, bytes)) { + throw new RuntimeException("Unexpected EOF from test program."); + } + + Conformance.ConformanceRequest request = + Conformance.ConformanceRequest.parseFrom(serializedInput); + Conformance.ConformanceResponse response = doTest(request); + byte[] serializedOutput = response.toByteArray(); + + writeLittleEndianIntToStdout(serializedOutput.length); + writeToStdout(serializedOutput); + + return true; + } + + public void run() throws Exception { + while (doTestIo()) { + this.testCount++; + } + + System.err.println("ConformanceJavaLite: received EOF from test runner after " + + this.testCount + " tests"); + } + + public static void main(String[] args) throws Exception { + new ConformanceJavaLite().run(); + } +} diff --git a/conformance/Makefile.am b/conformance/Makefile.am index 2a43113b1..31a9e4086 100644 --- a/conformance/Makefile.am +++ b/conformance/Makefile.am @@ -17,11 +17,114 @@ protoc_outputs = \ conformance.pb.h other_language_protoc_outputs = \ - conformance.rb \ - com/google/protobuf/conformance/Conformance.java \ conformance_pb2.py \ Conformance.pbobjc.h \ - Conformance.pbobjc.m + Conformance.pbobjc.m \ + conformance.rb \ + com/google/protobuf/Any.java \ + com/google/protobuf/AnyOrBuilder.java \ + com/google/protobuf/AnyProto.java \ + com/google/protobuf/BoolValue.java \ + com/google/protobuf/BoolValueOrBuilder.java \ + com/google/protobuf/BytesValue.java \ + com/google/protobuf/BytesValueOrBuilder.java \ + com/google/protobuf/conformance/Conformance.java \ + com/google/protobuf/DoubleValue.java \ + com/google/protobuf/DoubleValueOrBuilder.java \ + com/google/protobuf/Duration.java \ + com/google/protobuf/DurationOrBuilder.java \ + com/google/protobuf/DurationProto.java \ + com/google/protobuf/FieldMask.java \ + com/google/protobuf/FieldMaskOrBuilder.java \ + com/google/protobuf/FieldMaskProto.java \ + com/google/protobuf/FloatValue.java \ + com/google/protobuf/FloatValueOrBuilder.java \ + com/google/protobuf/Int32Value.java \ + com/google/protobuf/Int32ValueOrBuilder.java \ + com/google/protobuf/Int64Value.java \ + com/google/protobuf/Int64ValueOrBuilder.java \ + com/google/protobuf/ListValue.java \ + com/google/protobuf/ListValueOrBuilder.java \ + com/google/protobuf/NullValue.java \ + com/google/protobuf/StringValue.java \ + com/google/protobuf/StringValueOrBuilder.java \ + com/google/protobuf/Struct.java \ + com/google/protobuf/StructOrBuilder.java \ + com/google/protobuf/StructProto.java \ + com/google/protobuf/Timestamp.java \ + com/google/protobuf/TimestampOrBuilder.java \ + com/google/protobuf/TimestampProto.java \ + com/google/protobuf/UInt32Value.java \ + com/google/protobuf/UInt32ValueOrBuilder.java \ + com/google/protobuf/UInt64Value.java \ + com/google/protobuf/UInt64ValueOrBuilder.java \ + com/google/protobuf/Value.java \ + com/google/protobuf/ValueOrBuilder.java \ + com/google/protobuf/WrappersProto.java \ + google/protobuf/any.pb.cc \ + google/protobuf/any.pb.h \ + google/protobuf/any.rb \ + google/protobuf/any_pb2.py \ + google/protobuf/duration.pb.cc \ + google/protobuf/duration.pb.h \ + google/protobuf/duration.rb \ + google/protobuf/duration_pb2.py \ + google/protobuf/field_mask.pb.cc \ + google/protobuf/field_mask.pb.h \ + google/protobuf/field_mask.rb \ + google/protobuf/field_mask_pb2.py \ + google/protobuf/struct.pb.cc \ + google/protobuf/struct.pb.h \ + google/protobuf/struct.rb \ + google/protobuf/struct_pb2.py \ + google/protobuf/timestamp.pb.cc \ + google/protobuf/timestamp.pb.h \ + google/protobuf/timestamp.rb \ + google/protobuf/timestamp_pb2.py \ + google/protobuf/wrappers.pb.cc \ + google/protobuf/wrappers.pb.h \ + google/protobuf/wrappers.rb \ + google/protobuf/wrappers_pb2.py \ + lite/com/google/protobuf/Any.java \ + lite/com/google/protobuf/AnyOrBuilder.java \ + lite/com/google/protobuf/AnyProto.java \ + lite/com/google/protobuf/BoolValue.java \ + lite/com/google/protobuf/BoolValueOrBuilder.java \ + lite/com/google/protobuf/BytesValue.java \ + lite/com/google/protobuf/BytesValueOrBuilder.java \ + lite/com/google/protobuf/conformance/Conformance.java \ + lite/com/google/protobuf/DoubleValue.java \ + lite/com/google/protobuf/DoubleValueOrBuilder.java \ + lite/com/google/protobuf/Duration.java \ + lite/com/google/protobuf/DurationOrBuilder.java \ + lite/com/google/protobuf/DurationProto.java \ + lite/com/google/protobuf/FieldMask.java \ + lite/com/google/protobuf/FieldMaskOrBuilder.java \ + lite/com/google/protobuf/FieldMaskProto.java \ + lite/com/google/protobuf/FloatValue.java \ + lite/com/google/protobuf/FloatValueOrBuilder.java \ + lite/com/google/protobuf/Int32Value.java \ + lite/com/google/protobuf/Int32ValueOrBuilder.java \ + lite/com/google/protobuf/Int64Value.java \ + lite/com/google/protobuf/Int64ValueOrBuilder.java \ + lite/com/google/protobuf/ListValue.java \ + lite/com/google/protobuf/ListValueOrBuilder.java \ + lite/com/google/protobuf/NullValue.java \ + lite/com/google/protobuf/StringValue.java \ + lite/com/google/protobuf/StringValueOrBuilder.java \ + lite/com/google/protobuf/Struct.java \ + lite/com/google/protobuf/StructOrBuilder.java \ + lite/com/google/protobuf/StructProto.java \ + lite/com/google/protobuf/Timestamp.java \ + lite/com/google/protobuf/TimestampOrBuilder.java \ + lite/com/google/protobuf/TimestampProto.java \ + lite/com/google/protobuf/UInt32Value.java \ + lite/com/google/protobuf/UInt32ValueOrBuilder.java \ + lite/com/google/protobuf/UInt64Value.java \ + lite/com/google/protobuf/UInt64ValueOrBuilder.java \ + lite/com/google/protobuf/Value.java \ + lite/com/google/protobuf/ValueOrBuilder.java \ + lite/com/google/protobuf/WrappersProto.java bin_PROGRAMS = conformance-test-runner conformance-cpp @@ -30,6 +133,7 @@ bin_PROGRAMS = conformance-test-runner conformance-cpp # automatically. EXTRA_DIST = \ ConformanceJava.java \ + ConformanceJavaLite.java \ README.md \ conformance.proto \ conformance_python.py \ @@ -88,6 +192,7 @@ if USE_EXTERNAL_PROTOC protoc_middleman: $(conformance_protoc_inputs) $(well_known_type_protoc_inputs) $(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --ruby_out=. --objc_out=. --python_out=. $(conformance_protoc_inputs) $(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --ruby_out=. --python_out=. $(well_known_type_protoc_inputs) + $(PROTOC) -I$(srcdir) -I$(top_srcdir) --java_out=lite:lite $(conformance_protoc_inputs) $(well_known_type_protoc_inputs) touch protoc_middleman else @@ -98,6 +203,8 @@ else protoc_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(conformance_protoc_inputs) $(well_known_type_protoc_inputs) oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd --objc_out=$$oldpwd --python_out=$$oldpwd $(conformance_protoc_inputs) ) oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd --python_out=$$oldpwd $(well_known_type_protoc_inputs) ) + @mkdir -p lite + oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --java_out=lite:$$oldpwd/lite $(conformance_protoc_inputs) $(well_known_type_protoc_inputs) ) touch protoc_middleman endif @@ -108,7 +215,7 @@ $(other_language_protoc_outputs): protoc_middleman BUILT_SOURCES = $(protoc_outputs) $(other_language_protoc_outputs) -CLEANFILES = $(protoc_outputs) protoc_middleman javac_middleman conformance-java conformance-csharp $(other_language_protoc_outputs) +CLEANFILES = $(protoc_outputs) protoc_middleman javac_middleman conformance-java javac_middleman_lite conformance-java-lite conformance-csharp $(other_language_protoc_outputs) MAINTAINERCLEANFILES = \ Makefile.in @@ -123,6 +230,16 @@ conformance-java: javac_middleman @jar=`ls ../java/util/target/*jar-with-dependencies.jar` && echo java -classpath .:../java/target/classes:$$jar ConformanceJava '$$@' >> conformance-java @chmod +x conformance-java +javac_middleman_lite: ConformanceJavaLite.java protoc_middleman $(other_language_protoc_outputs) + javac -classpath ../java/lite/target/classes:lite ConformanceJavaLite.java lite/com/google/protobuf/conformance/Conformance.java + @touch javac_middleman_lite + +conformance-java-lite: javac_middleman_lite + @echo "Writing shortcut script conformance-java-lite..." + @echo '#! /bin/sh' > conformance-java-lite + @echo java -classpath .:../java/lite/target/classes:lite ConformanceJavaLite '$$@' >> conformance-java-lite + @chmod +x conformance-java-lite + # Currently the conformance code is alongside the rest of the C# # source, as it's easier to maintain there. We assume we've already # built that, so we just need a script to run it. @@ -139,6 +256,9 @@ test_cpp: protoc_middleman conformance-test-runner conformance-cpp test_java: protoc_middleman conformance-test-runner conformance-java ./conformance-test-runner --failure_list failure_list_java.txt ./conformance-java +test_java_lite: protoc_middleman conformance-test-runner conformance-java-lite + ./conformance-test-runner ./conformance-java-lite + test_csharp: protoc_middleman conformance-test-runner conformance-csharp ./conformance-test-runner --failure_list failure_list_csharp.txt ./conformance-csharp diff --git a/conformance/failure_list_ruby.txt b/conformance/failure_list_ruby.txt index ade0ff4c9..7c12da061 100644 --- a/conformance/failure_list_ruby.txt +++ b/conformance/failure_list_ruby.txt @@ -80,7 +80,6 @@ JsonInput.Int32FieldMaxFloatValue.JsonOutput JsonInput.Int32FieldMaxFloatValue.ProtobufOutput JsonInput.Int32FieldMinFloatValue.JsonOutput JsonInput.Int32FieldMinFloatValue.ProtobufOutput -JsonInput.Int32FieldMinValue.JsonOutput JsonInput.Int32FieldStringValue.JsonOutput JsonInput.Int32FieldStringValue.ProtobufOutput JsonInput.Int32FieldStringValueEscaped.JsonOutput @@ -125,7 +124,6 @@ JsonInput.OptionalUint64Wrapper.ProtobufOutput JsonInput.OptionalWrapperTypesWithNonDefaultValue.JsonOutput JsonInput.OptionalWrapperTypesWithNonDefaultValue.ProtobufOutput JsonInput.OriginalProtoFieldName.JsonOutput -JsonInput.OriginalProtoFieldName.ProtobufOutput JsonInput.PrimitiveRepeatedField.JsonOutput JsonInput.PrimitiveRepeatedField.ProtobufOutput JsonInput.RepeatedBoolWrapper.JsonOutput diff --git a/csharp/Google.Protobuf.Tools.nuspec b/csharp/Google.Protobuf.Tools.nuspec new file mode 100644 index 000000000..2b66b081e --- /dev/null +++ b/csharp/Google.Protobuf.Tools.nuspec @@ -0,0 +1,37 @@ + + + + Google.Protobuf.Tools + Google Protocol Buffers tools + Tools for Protocol Buffers - Google's data interchange format. + See project site for more info. + 3.0.0-beta2 + Google Inc. + protobuf-packages + https://github.com/google/protobuf/blob/master/LICENSE + https://github.com/google/protobuf + false + Tools for Protocol Buffers + Copyright 2015, Google Inc. + Protocol Buffers Binary Serialization Format Google proto proto3 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/csharp/protos/unittest_issues.proto b/csharp/protos/unittest_issues.proto index 989b3dc42..6c9f76344 100644 --- a/csharp/protos/unittest_issues.proto +++ b/csharp/protos/unittest_issues.proto @@ -116,4 +116,11 @@ message TestJsonFieldOrdering { string o2_string = 3; } -} \ No newline at end of file +} + +message TestJsonName { + // Message for testing the effects for of the json_name option + string name = 1; + string description = 2 [json_name = "desc"]; + string guid = 3 [json_name = "exid"]; +} diff --git a/csharp/src/AddressBook/AddPerson.cs b/csharp/src/AddressBook/AddPerson.cs index 6eeb9f6e8..62d1788d5 100644 --- a/csharp/src/AddressBook/AddPerson.cs +++ b/csharp/src/AddressBook/AddPerson.cs @@ -73,13 +73,13 @@ namespace Google.Protobuf.Examples.AddressBook switch (type) { case "mobile": - phoneNumber.Type = Person.Types.PhoneType.MOBILE; + phoneNumber.Type = Person.Types.PhoneType.Mobile; break; case "home": - phoneNumber.Type = Person.Types.PhoneType.HOME; + phoneNumber.Type = Person.Types.PhoneType.Home; break; case "work": - phoneNumber.Type = Person.Types.PhoneType.WORK; + phoneNumber.Type = Person.Types.PhoneType.Work; break; default: output.Write("Unknown phone type. Using default."); diff --git a/csharp/src/AddressBook/Addressbook.cs b/csharp/src/AddressBook/Addressbook.cs index 166dd49a9..362e1cb6a 100644 --- a/csharp/src/AddressBook/Addressbook.cs +++ b/csharp/src/AddressBook/Addressbook.cs @@ -228,9 +228,9 @@ namespace Google.Protobuf.Examples.AddressBook { [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] public static partial class Types { public enum PhoneType { - MOBILE = 0, - HOME = 1, - WORK = 2, + [pbr::OriginalName("MOBILE")] Mobile = 0, + [pbr::OriginalName("HOME")] Home = 1, + [pbr::OriginalName("WORK")] Work = 2, } [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] @@ -273,7 +273,7 @@ namespace Google.Protobuf.Examples.AddressBook { /// Field number for the "type" field. public const int TypeFieldNumber = 2; - private global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType type_ = global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE; + private global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType type_ = 0; public global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType Type { get { return type_; } set { @@ -300,7 +300,7 @@ namespace Google.Protobuf.Examples.AddressBook { public override int GetHashCode() { int hash = 1; if (Number.Length != 0) hash ^= Number.GetHashCode(); - if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE) hash ^= Type.GetHashCode(); + if (Type != 0) hash ^= Type.GetHashCode(); return hash; } @@ -313,7 +313,7 @@ namespace Google.Protobuf.Examples.AddressBook { output.WriteRawTag(10); output.WriteString(Number); } - if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE) { + if (Type != 0) { output.WriteRawTag(16); output.WriteEnum((int) Type); } @@ -324,7 +324,7 @@ namespace Google.Protobuf.Examples.AddressBook { if (Number.Length != 0) { size += 1 + pb::CodedOutputStream.ComputeStringSize(Number); } - if (Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE) { + if (Type != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Type); } return size; @@ -337,7 +337,7 @@ namespace Google.Protobuf.Examples.AddressBook { if (other.Number.Length != 0) { Number = other.Number; } - if (other.Type != global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneType.MOBILE) { + if (other.Type != 0) { Type = other.Type; } } diff --git a/csharp/src/AddressBook/ListPeople.cs b/csharp/src/AddressBook/ListPeople.cs index 3979430fe..3758c1bce 100644 --- a/csharp/src/AddressBook/ListPeople.cs +++ b/csharp/src/AddressBook/ListPeople.cs @@ -55,13 +55,13 @@ namespace Google.Protobuf.Examples.AddressBook { switch (phoneNumber.Type) { - case Person.Types.PhoneType.MOBILE: + case Person.Types.PhoneType.Mobile: Console.Write(" Mobile phone #: "); break; - case Person.Types.PhoneType.HOME: + case Person.Types.PhoneType.Home: Console.Write(" Home phone #: "); break; - case Person.Types.PhoneType.WORK: + case Person.Types.PhoneType.Work: Console.Write(" Work phone #: "); break; } diff --git a/csharp/src/Google.Protobuf.Conformance/Conformance.cs b/csharp/src/Google.Protobuf.Conformance/Conformance.cs index 7d85d28cf..1674a6734 100644 --- a/csharp/src/Google.Protobuf.Conformance/Conformance.cs +++ b/csharp/src/Google.Protobuf.Conformance/Conformance.cs @@ -199,15 +199,15 @@ namespace Conformance { } #region Enums public enum WireFormat { - UNSPECIFIED = 0, - PROTOBUF = 1, - JSON = 2, + [pbr::OriginalName("UNSPECIFIED")] Unspecified = 0, + [pbr::OriginalName("PROTOBUF")] Protobuf = 1, + [pbr::OriginalName("JSON")] Json = 2, } public enum ForeignEnum { - FOREIGN_FOO = 0, - FOREIGN_BAR = 1, - FOREIGN_BAZ = 2, + [pbr::OriginalName("FOREIGN_FOO")] ForeignFoo = 0, + [pbr::OriginalName("FOREIGN_BAR")] ForeignBar = 1, + [pbr::OriginalName("FOREIGN_BAZ")] ForeignBaz = 2, } #endregion @@ -278,7 +278,7 @@ namespace Conformance { /// Field number for the "requested_output_format" field. public const int RequestedOutputFormatFieldNumber = 3; - private global::Conformance.WireFormat requestedOutputFormat_ = global::Conformance.WireFormat.UNSPECIFIED; + private global::Conformance.WireFormat requestedOutputFormat_ = 0; /// /// Which format should the testee serialize its message to? /// @@ -328,7 +328,7 @@ namespace Conformance { int hash = 1; if (payloadCase_ == PayloadOneofCase.ProtobufPayload) hash ^= ProtobufPayload.GetHashCode(); if (payloadCase_ == PayloadOneofCase.JsonPayload) hash ^= JsonPayload.GetHashCode(); - if (RequestedOutputFormat != global::Conformance.WireFormat.UNSPECIFIED) hash ^= RequestedOutputFormat.GetHashCode(); + if (RequestedOutputFormat != 0) hash ^= RequestedOutputFormat.GetHashCode(); hash ^= (int) payloadCase_; return hash; } @@ -346,7 +346,7 @@ namespace Conformance { output.WriteRawTag(18); output.WriteString(JsonPayload); } - if (RequestedOutputFormat != global::Conformance.WireFormat.UNSPECIFIED) { + if (RequestedOutputFormat != 0) { output.WriteRawTag(24); output.WriteEnum((int) RequestedOutputFormat); } @@ -360,7 +360,7 @@ namespace Conformance { if (payloadCase_ == PayloadOneofCase.JsonPayload) { size += 1 + pb::CodedOutputStream.ComputeStringSize(JsonPayload); } - if (RequestedOutputFormat != global::Conformance.WireFormat.UNSPECIFIED) { + if (RequestedOutputFormat != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) RequestedOutputFormat); } return size; @@ -370,7 +370,7 @@ namespace Conformance { if (other == null) { return; } - if (other.RequestedOutputFormat != global::Conformance.WireFormat.UNSPECIFIED) { + if (other.RequestedOutputFormat != 0) { RequestedOutputFormat = other.RequestedOutputFormat; } switch (other.PayloadCase) { @@ -1044,7 +1044,7 @@ namespace Conformance { /// Field number for the "optional_nested_enum" field. public const int OptionalNestedEnumFieldNumber = 21; - private global::Conformance.TestAllTypes.Types.NestedEnum optionalNestedEnum_ = global::Conformance.TestAllTypes.Types.NestedEnum.FOO; + private global::Conformance.TestAllTypes.Types.NestedEnum optionalNestedEnum_ = 0; public global::Conformance.TestAllTypes.Types.NestedEnum OptionalNestedEnum { get { return optionalNestedEnum_; } set { @@ -1054,7 +1054,7 @@ namespace Conformance { /// Field number for the "optional_foreign_enum" field. public const int OptionalForeignEnumFieldNumber = 22; - private global::Conformance.ForeignEnum optionalForeignEnum_ = global::Conformance.ForeignEnum.FOREIGN_FOO; + private global::Conformance.ForeignEnum optionalForeignEnum_ = 0; public global::Conformance.ForeignEnum OptionalForeignEnum { get { return optionalForeignEnum_; } set { @@ -2079,8 +2079,8 @@ namespace Conformance { if (OptionalBytes.Length != 0) hash ^= OptionalBytes.GetHashCode(); if (optionalNestedMessage_ != null) hash ^= OptionalNestedMessage.GetHashCode(); if (optionalForeignMessage_ != null) hash ^= OptionalForeignMessage.GetHashCode(); - if (OptionalNestedEnum != global::Conformance.TestAllTypes.Types.NestedEnum.FOO) hash ^= OptionalNestedEnum.GetHashCode(); - if (OptionalForeignEnum != global::Conformance.ForeignEnum.FOREIGN_FOO) hash ^= OptionalForeignEnum.GetHashCode(); + if (OptionalNestedEnum != 0) hash ^= OptionalNestedEnum.GetHashCode(); + if (OptionalForeignEnum != 0) hash ^= OptionalForeignEnum.GetHashCode(); if (OptionalStringPiece.Length != 0) hash ^= OptionalStringPiece.GetHashCode(); if (OptionalCord.Length != 0) hash ^= OptionalCord.GetHashCode(); if (recursiveMessage_ != null) hash ^= RecursiveMessage.GetHashCode(); @@ -2247,11 +2247,11 @@ namespace Conformance { output.WriteRawTag(154, 1); output.WriteMessage(OptionalForeignMessage); } - if (OptionalNestedEnum != global::Conformance.TestAllTypes.Types.NestedEnum.FOO) { + if (OptionalNestedEnum != 0) { output.WriteRawTag(168, 1); output.WriteEnum((int) OptionalNestedEnum); } - if (OptionalForeignEnum != global::Conformance.ForeignEnum.FOREIGN_FOO) { + if (OptionalForeignEnum != 0) { output.WriteRawTag(176, 1); output.WriteEnum((int) OptionalForeignEnum); } @@ -2492,10 +2492,10 @@ namespace Conformance { if (optionalForeignMessage_ != null) { size += 2 + pb::CodedOutputStream.ComputeMessageSize(OptionalForeignMessage); } - if (OptionalNestedEnum != global::Conformance.TestAllTypes.Types.NestedEnum.FOO) { + if (OptionalNestedEnum != 0) { size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) OptionalNestedEnum); } - if (OptionalForeignEnum != global::Conformance.ForeignEnum.FOREIGN_FOO) { + if (OptionalForeignEnum != 0) { size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) OptionalForeignEnum); } if (OptionalStringPiece.Length != 0) { @@ -2719,10 +2719,10 @@ namespace Conformance { } OptionalForeignMessage.MergeFrom(other.OptionalForeignMessage); } - if (other.OptionalNestedEnum != global::Conformance.TestAllTypes.Types.NestedEnum.FOO) { + if (other.OptionalNestedEnum != 0) { OptionalNestedEnum = other.OptionalNestedEnum; } - if (other.OptionalForeignEnum != global::Conformance.ForeignEnum.FOREIGN_FOO) { + if (other.OptionalForeignEnum != 0) { OptionalForeignEnum = other.OptionalForeignEnum; } if (other.OptionalStringPiece.Length != 0) { @@ -3448,13 +3448,13 @@ namespace Conformance { [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] public static partial class Types { public enum NestedEnum { - FOO = 0, - BAR = 1, - BAZ = 2, + [pbr::OriginalName("FOO")] Foo = 0, + [pbr::OriginalName("BAR")] Bar = 1, + [pbr::OriginalName("BAZ")] Baz = 2, /// /// Intentionally negative. /// - NEG = -1, + [pbr::OriginalName("NEG")] Neg = -1, } [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] diff --git a/csharp/src/Google.Protobuf.Conformance/Program.cs b/csharp/src/Google.Protobuf.Conformance/Program.cs index f3f7e2957..19827c484 100644 --- a/csharp/src/Google.Protobuf.Conformance/Program.cs +++ b/csharp/src/Google.Protobuf.Conformance/Program.cs @@ -109,10 +109,10 @@ namespace Google.Protobuf.Conformance { switch (request.RequestedOutputFormat) { - case global::Conformance.WireFormat.JSON: + case global::Conformance.WireFormat.Json: var formatter = new JsonFormatter(new JsonFormatter.Settings(false, typeRegistry)); return new ConformanceResponse { JsonPayload = formatter.Format(message) }; - case global::Conformance.WireFormat.PROTOBUF: + case global::Conformance.WireFormat.Protobuf: return new ConformanceResponse { ProtobufPayload = message.ToByteString() }; default: throw new Exception("Unsupported request output format: " + request.PayloadCase); diff --git a/csharp/src/Google.Protobuf.Test/FieldCodecTest.cs b/csharp/src/Google.Protobuf.Test/FieldCodecTest.cs index 38ba227f6..c616470e8 100644 --- a/csharp/src/Google.Protobuf.Test/FieldCodecTest.cs +++ b/csharp/src/Google.Protobuf.Test/FieldCodecTest.cs @@ -58,7 +58,7 @@ namespace Google.Protobuf new FieldCodecTestData(FieldCodec.ForFloat(100), 1234.5f, "Float"), new FieldCodecTestData(FieldCodec.ForDouble(100), 1234567890.5d, "Double"), new FieldCodecTestData( - FieldCodec.ForEnum(100, t => (int) t, t => (ForeignEnum) t), ForeignEnum.FOREIGN_BAZ, "Enum"), + FieldCodec.ForEnum(100, t => (int) t, t => (ForeignEnum) t), ForeignEnum.ForeignBaz, "Enum"), new FieldCodecTestData( FieldCodec.ForMessage(100, ForeignMessage.Parser), new ForeignMessage { C = 10 }, "Message"), }; diff --git a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs index 5c2a052b6..8b153d691 100644 --- a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs +++ b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs @@ -66,13 +66,13 @@ namespace Google.Protobuf Assert.AreEqual(0, message.SingleFixed32); Assert.AreEqual(0L, message.SingleFixed64); Assert.AreEqual(0.0f, message.SingleFloat); - Assert.AreEqual(ForeignEnum.FOREIGN_UNSPECIFIED, message.SingleForeignEnum); + Assert.AreEqual(ForeignEnum.ForeignUnspecified, message.SingleForeignEnum); Assert.IsNull(message.SingleForeignMessage); - Assert.AreEqual(ImportEnum.IMPORT_ENUM_UNSPECIFIED, message.SingleImportEnum); + 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.NESTED_ENUM_UNSPECIFIED, message.SingleNestedEnum); + Assert.AreEqual(TestAllTypes.Types.NestedEnum.Unspecified, message.SingleNestedEnum); Assert.IsNull(message.SingleNestedMessage); Assert.IsNull(message.SinglePublicImportMessage); Assert.AreEqual(0, message.SingleSfixed32); @@ -145,13 +145,13 @@ namespace Google.Protobuf SingleFixed32 = 23, SingleFixed64 = 1234567890123, SingleFloat = 12.25f, - SingleForeignEnum = ForeignEnum.FOREIGN_BAR, + SingleForeignEnum = ForeignEnum.ForeignBar, SingleForeignMessage = new ForeignMessage { C = 10 }, - SingleImportEnum = ImportEnum.IMPORT_BAZ, + SingleImportEnum = ImportEnum.ImportBaz, SingleImportMessage = new ImportMessage { D = 20 }, SingleInt32 = 100, SingleInt64 = 3210987654321, - SingleNestedEnum = TestAllTypes.Types.NestedEnum.FOO, + SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo, SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 }, SinglePublicImportMessage = new PublicImportMessage { E = 54 }, SingleSfixed32 = -123, @@ -179,13 +179,13 @@ namespace Google.Protobuf RepeatedFixed32 = { uint.MaxValue, 23 }, RepeatedFixed64 = { ulong.MaxValue, 1234567890123 }, RepeatedFloat = { 100f, 12.25f }, - RepeatedForeignEnum = { ForeignEnum.FOREIGN_FOO, ForeignEnum.FOREIGN_BAR }, + RepeatedForeignEnum = { ForeignEnum.ForeignFoo, ForeignEnum.ForeignBar }, RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } }, - RepeatedImportEnum = { ImportEnum.IMPORT_BAZ, ImportEnum.IMPORT_ENUM_UNSPECIFIED }, + 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 }, + 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 }, @@ -224,8 +224,8 @@ namespace Google.Protobuf { 5, new ForeignMessage() }, }, MapInt32Enum = { - { 1, MapEnum.MAP_ENUM_BAR }, - { 2000, MapEnum.MAP_ENUM_FOO } + { 1, MapEnum.Bar }, + { 2000, MapEnum.Foo } } }; @@ -249,7 +249,7 @@ namespace Google.Protobuf Assert.AreEqual(1, parsed.MapInt32Bytes.Count); Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]); } - + [Test] public void MapWithOnlyValue() { @@ -449,7 +449,7 @@ namespace Google.Protobuf SingleFloat = 12.25f, SingleInt32 = 100, SingleInt64 = 3210987654321, - SingleNestedEnum = TestAllTypes.Types.NestedEnum.FOO, + SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo, SingleSfixed32 = -123, SingleSfixed64 = -12345678901234, SingleSint32 = -456, @@ -479,7 +479,7 @@ namespace Google.Protobuf RepeatedFloat = { 100f, 12.25f }, RepeatedInt32 = { 100, 200 }, RepeatedInt64 = { 3210987654321, long.MaxValue }, - RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.FOO, TestAllTypes.Types.NestedEnum.NEG }, + RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg }, RepeatedSfixed32 = { -123, 123 }, RepeatedSfixed64 = { -12345678901234, 12345678901234 }, RepeatedSint32 = { -456, 100 }, @@ -670,7 +670,7 @@ namespace Google.Protobuf { // 130, 3 is the message tag // 1 is the data length - but there's no data. - var data = new byte[] { 130, 3, 1 }; + var data = new byte[] { 130, 3, 1 }; Assert.Throws(() => TestAllTypes.Parser.ParseFrom(data)); } @@ -720,4 +720,4 @@ namespace Google.Protobuf Assert.AreEqual("{ \"c\": 31 }", writer.ToString()); } } -} +} \ No newline at end of file diff --git a/csharp/src/Google.Protobuf.Test/IssuesTest.cs b/csharp/src/Google.Protobuf.Test/IssuesTest.cs index a03500357..a38d6b08b 100644 --- a/csharp/src/Google.Protobuf.Test/IssuesTest.cs +++ b/csharp/src/Google.Protobuf.Test/IssuesTest.cs @@ -59,5 +59,24 @@ namespace Google.Protobuf // 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("{ \"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/src/Google.Protobuf.Test/JsonFormatterTest.cs b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs index 42455043e..827a75956 100644 --- a/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs +++ b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs @@ -75,13 +75,13 @@ namespace Google.Protobuf SingleFixed32 = 23, SingleFixed64 = 1234567890123, SingleFloat = 12.25f, - SingleForeignEnum = ForeignEnum.FOREIGN_BAR, + SingleForeignEnum = ForeignEnum.ForeignBar, SingleForeignMessage = new ForeignMessage { C = 10 }, - SingleImportEnum = ImportEnum.IMPORT_BAZ, + SingleImportEnum = ImportEnum.ImportBaz, SingleImportMessage = new ImportMessage { D = 20 }, SingleInt32 = 100, SingleInt64 = 3210987654321, - SingleNestedEnum = TestAllTypes.Types.NestedEnum.FOO, + SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo, SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 }, SinglePublicImportMessage = new PublicImportMessage { E = 54 }, SingleSfixed32 = -123, @@ -174,14 +174,14 @@ namespace Google.Protobuf [Test] public void UnknownEnumValueNumeric_RepeatedField() { - var message = new TestAllTypes { RepeatedForeignEnum = { ForeignEnum.FOREIGN_BAZ, (ForeignEnum) 100, ForeignEnum.FOREIGN_FOO } }; + 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.MAP_ENUM_FOO }, { 2, (MapEnum) 100 }, { 3, MapEnum.MAP_ENUM_BAR } } }; + 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)); } @@ -321,7 +321,6 @@ namespace Google.Protobuf [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.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)] @@ -481,6 +480,15 @@ namespace Google.Protobuf AssertJson("{ '@type': 'type.googleapis.com/protobuf_unittest.TestAllTypes', 'singleInt32': 10, 'singleNestedMessage': { 'bb': 20 } }", formatter.Format(any)); } + [Test] + public void AnyMessageType_CustomPrefix() + { + var formatter = new JsonFormatter(new JsonFormatter.Settings(false, TypeRegistry.FromMessages(TestAllTypes.Descriptor))); + var message = new TestAllTypes { SingleInt32 = 10 }; + var any = Any.Pack(message, "foo.bar/baz"); + AssertJson("{ '@type': 'foo.bar/baz/protobuf_unittest.TestAllTypes', 'singleInt32': 10 }", formatter.Format(any)); + } + [Test] public void AnyNested() { diff --git a/csharp/src/Google.Protobuf.Test/JsonParserTest.cs b/csharp/src/Google.Protobuf.Test/JsonParserTest.cs index d21da58a6..c3ad851b8 100644 --- a/csharp/src/Google.Protobuf.Test/JsonParserTest.cs +++ b/csharp/src/Google.Protobuf.Test/JsonParserTest.cs @@ -142,8 +142,8 @@ namespace Google.Protobuf [TestCase(typeof(DoubleValue), "1.5", 1.5d)] public void Wrappers_Standalone(System.Type wrapperType, string json, object expectedValue) { - IMessage parsed = (IMessage) Activator.CreateInstance(wrapperType); - IMessage expected = (IMessage) Activator.CreateInstance(wrapperType); + IMessage parsed = (IMessage)Activator.CreateInstance(wrapperType); + IMessage expected = (IMessage)Activator.CreateInstance(wrapperType); JsonParser.Default.Merge(parsed, "null"); Assert.AreEqual(expected, parsed); @@ -640,7 +640,7 @@ namespace Google.Protobuf var parsed = Timestamp.Parser.ParseJson(json); Assert.AreEqual(WrapInQuotes(expectedFormatted), parsed.ToString()); } - + [Test] [TestCase("2015-10-09 14:46:23.123456789Z", Description = "No T between date and time")] [TestCase("2015/10/09T14:46:23.123456789Z", Description = "Wrong date separators")] @@ -810,6 +810,17 @@ namespace Google.Protobuf Assert.AreEqual(original, parser.Parse(valueFirstJson)); } + [Test] + public void Any_CustomPrefix() + { + var registry = TypeRegistry.FromMessages(TestAllTypes.Descriptor); + var message = new TestAllTypes { SingleInt32 = 10 }; + var original = Any.Pack(message, "custom.prefix/middle-part"); + var parser = new JsonParser(new JsonParser.Settings(10, registry)); + string json = "{ \"@type\": \"custom.prefix/middle-part/protobuf_unittest.TestAllTypes\", \"singleInt32\": 10 }"; + Assert.AreEqual(original, parser.Parse(json)); + } + [Test] public void Any_UnknownType() { @@ -886,9 +897,9 @@ namespace Google.Protobuf } [Test] - [TestCase("\"FOREIGN_BAR\"", ForeignEnum.FOREIGN_BAR)] - [TestCase("5", ForeignEnum.FOREIGN_BAR)] - [TestCase("100", (ForeignEnum) 100)] + [TestCase("\"FOREIGN_BAR\"", ForeignEnum.ForeignBar)] + [TestCase("5", ForeignEnum.ForeignBar)] + [TestCase("100", (ForeignEnum)100)] public void EnumValid(string value, ForeignEnum expectedValue) { string json = "{ \"singleForeignEnum\": " + value + " }"; @@ -922,4 +933,4 @@ namespace Google.Protobuf return '"' + text + '"'; } } -} +} \ No newline at end of file diff --git a/csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs b/csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs index a0a622276..527ab3361 100644 --- a/csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs +++ b/csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs @@ -249,7 +249,6 @@ namespace Google.Protobuf [TestCase("[,", 1)] [TestCase("{", 1)] [TestCase("{,", 1)] - [TestCase("{", 1)] [TestCase("{[", 1)] [TestCase("{{", 1)] [TestCase("{0", 1)] diff --git a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs index 086a4e981..52d5a6769 100644 --- a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs +++ b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs @@ -195,7 +195,7 @@ namespace Google.Protobuf.Reflection Assert.AreEqual(value, enumType.Values[1]); Assert.AreEqual("FOREIGN_FOO", value.Name); Assert.AreEqual(4, value.Number); - Assert.AreEqual((int) ForeignEnum.FOREIGN_FOO, value.Number); + Assert.AreEqual((int) ForeignEnum.ForeignFoo, value.Number); Assert.AreEqual(value, enumType.FindValueByNumber(4)); Assert.Null(enumType.FindValueByName("NO_SUCH_VALUE")); for (int i = 0; i < enumType.Values.Count; i++) diff --git a/csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs b/csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs index 936e41c68..a488af30d 100644 --- a/csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs +++ b/csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs @@ -128,7 +128,7 @@ namespace Google.Protobuf.Reflection fields[TestAllTypes.SingleInt32FieldNumber].Accessor.SetValue(message, 500); fields[TestAllTypes.SingleStringFieldNumber].Accessor.SetValue(message, "It's a string"); fields[TestAllTypes.SingleBytesFieldNumber].Accessor.SetValue(message, ByteString.CopyFrom(99, 98, 97)); - fields[TestAllTypes.SingleForeignEnumFieldNumber].Accessor.SetValue(message, ForeignEnum.FOREIGN_FOO); + fields[TestAllTypes.SingleForeignEnumFieldNumber].Accessor.SetValue(message, ForeignEnum.ForeignFoo); fields[TestAllTypes.SingleForeignMessageFieldNumber].Accessor.SetValue(message, new ForeignMessage { C = 12345 }); fields[TestAllTypes.SingleDoubleFieldNumber].Accessor.SetValue(message, 20150701.5); @@ -138,7 +138,7 @@ namespace Google.Protobuf.Reflection SingleInt32 = 500, SingleString = "It's a string", SingleBytes = ByteString.CopyFrom(99, 98, 97), - SingleForeignEnum = ForeignEnum.FOREIGN_FOO, + SingleForeignEnum = ForeignEnum.ForeignFoo, SingleForeignMessage = new ForeignMessage { C = 12345 }, SingleDouble = 20150701.5 }; diff --git a/csharp/src/Google.Protobuf.Test/SampleMessages.cs b/csharp/src/Google.Protobuf.Test/SampleMessages.cs index 8a9c7f867..ffa4e2a7c 100644 --- a/csharp/src/Google.Protobuf.Test/SampleMessages.cs +++ b/csharp/src/Google.Protobuf.Test/SampleMessages.cs @@ -54,13 +54,13 @@ namespace Google.Protobuf SingleFixed32 = 23, SingleFixed64 = 1234567890123, SingleFloat = 12.25f, - SingleForeignEnum = ForeignEnum.FOREIGN_BAR, + SingleForeignEnum = ForeignEnum.ForeignBar, SingleForeignMessage = new ForeignMessage { C = 10 }, - SingleImportEnum = ImportEnum.IMPORT_BAZ, + SingleImportEnum = ImportEnum.ImportBaz, SingleImportMessage = new ImportMessage { D = 20 }, SingleInt32 = 100, SingleInt64 = 3210987654321, - SingleNestedEnum = TestAllTypes.Types.NestedEnum.FOO, + SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo, SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 }, SinglePublicImportMessage = new PublicImportMessage { E = 54 }, SingleSfixed32 = -123, @@ -76,13 +76,13 @@ namespace Google.Protobuf RepeatedFixed32 = { UInt32.MaxValue, 23 }, RepeatedFixed64 = { UInt64.MaxValue, 1234567890123 }, RepeatedFloat = { 100f, 12.25f }, - RepeatedForeignEnum = { ForeignEnum.FOREIGN_FOO, ForeignEnum.FOREIGN_BAR }, + RepeatedForeignEnum = { ForeignEnum.ForeignFoo, ForeignEnum.ForeignBar }, RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } }, - RepeatedImportEnum = { ImportEnum.IMPORT_BAZ, ImportEnum.IMPORT_ENUM_UNSPECIFIED }, + RepeatedImportEnum = { ImportEnum.ImportBaz, ImportEnum.Unspecified }, RepeatedImportMessage = { new ImportMessage { D = 20 }, new ImportMessage { D = 25 } }, RepeatedInt32 = { 100, 200 }, RepeatedInt64 = { 3210987654321, Int64.MaxValue }, - RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.FOO, TestAllTypes.Types.NestedEnum.NEG }, + 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 }, @@ -92,8 +92,8 @@ namespace Google.Protobuf RepeatedString = { "foo", "bar" }, RepeatedUint32 = { UInt32.MaxValue, UInt32.MinValue }, RepeatedUint64 = { UInt64.MaxValue, UInt32.MinValue }, - OneofString = "Oneof string" + OneofString = "Oneof string" }; } } -} +} \ No newline at end of file diff --git a/csharp/src/Google.Protobuf.Test/TestCornerCases.cs b/csharp/src/Google.Protobuf.Test/TestCornerCases.cs index 03fa18559..248f5fa91 100644 --- a/csharp/src/Google.Protobuf.Test/TestCornerCases.cs +++ b/csharp/src/Google.Protobuf.Test/TestCornerCases.cs @@ -43,8 +43,8 @@ namespace Google.Protobuf NegativeEnumMessage msg = new NegativeEnumMessage { Value = NegativeEnum.MinusOne, - Values = { NegativeEnum.NEGATIVE_ENUM_ZERO, NegativeEnum.MinusOne, NegativeEnum.FiveBelow }, - PackedValues = { NegativeEnum.NEGATIVE_ENUM_ZERO, NegativeEnum.MinusOne, NegativeEnum.FiveBelow } + Values = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow }, + PackedValues = { NegativeEnum.Zero, NegativeEnum.MinusOne, NegativeEnum.FiveBelow } }; Assert.AreEqual(58, msg.CalculateSize()); diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs b/csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs index 27c04787c..3ba4a2b66 100644 --- a/csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs +++ b/csharp/src/Google.Protobuf.Test/TestProtos/MapUnittestProto3.cs @@ -164,9 +164,9 @@ namespace Google.Protobuf.TestProtos { } #region Enums public enum MapEnum { - MAP_ENUM_FOO = 0, - MAP_ENUM_BAR = 1, - MAP_ENUM_BAZ = 2, + [pbr::OriginalName("MAP_ENUM_FOO")] Foo = 0, + [pbr::OriginalName("MAP_ENUM_BAR")] Bar = 1, + [pbr::OriginalName("MAP_ENUM_BAZ")] Baz = 2, } #endregion @@ -1358,7 +1358,7 @@ namespace Google.Protobuf.TestProtos { [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] public static partial class Types { public enum Type { - TYPE_FOO = 0, + [pbr::OriginalName("TYPE_FOO")] Foo = 0, } } diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs index 7b824dc71..263e17c02 100644 --- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs +++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestImportProto3.cs @@ -42,10 +42,10 @@ namespace Google.Protobuf.TestProtos { } #region Enums public enum ImportEnum { - IMPORT_ENUM_UNSPECIFIED = 0, - IMPORT_FOO = 7, - IMPORT_BAR = 8, - IMPORT_BAZ = 9, + [pbr::OriginalName("IMPORT_ENUM_UNSPECIFIED")] Unspecified = 0, + [pbr::OriginalName("IMPORT_FOO")] ImportFoo = 7, + [pbr::OriginalName("IMPORT_BAR")] ImportBar = 8, + [pbr::OriginalName("IMPORT_BAZ")] ImportBaz = 9, } #endregion diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs index 16176a33f..7d4451b09 100644 --- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs +++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestIssues.cs @@ -42,10 +42,12 @@ namespace UnitTest.Issues.TestProtos { "CgtwbGFpbl9pbnQzMhgEIAEoBRITCglvMV9zdHJpbmcYAiABKAlIABISCghv", "MV9pbnQzMhgFIAEoBUgAEhQKDHBsYWluX3N0cmluZxgBIAEoCRISCghvMl9p", "bnQzMhgGIAEoBUgBEhMKCW8yX3N0cmluZxgDIAEoCUgBQgQKAm8xQgQKAm8y", - "KlUKDE5lZ2F0aXZlRW51bRIWChJORUdBVElWRV9FTlVNX1pFUk8QABIWCglG", - "aXZlQmVsb3cQ+///////////ARIVCghNaW51c09uZRD///////////8BKi4K", - "DkRlcHJlY2F0ZWRFbnVtEhMKD0RFUFJFQ0FURURfWkVSTxAAEgcKA29uZRAB", - "Qh9IAaoCGlVuaXRUZXN0Lklzc3Vlcy5UZXN0UHJvdG9zYgZwcm90bzM=")); + "IksKDFRlc3RKc29uTmFtZRIMCgRuYW1lGAEgASgJEhkKC2Rlc2NyaXB0aW9u", + "GAIgASgJUgRkZXNjEhIKBGd1aWQYAyABKAlSBGV4aWQqVQoMTmVnYXRpdmVF", + "bnVtEhYKEk5FR0FUSVZFX0VOVU1fWkVSTxAAEhYKCUZpdmVCZWxvdxD7////", + "//////8BEhUKCE1pbnVzT25lEP///////////wEqLgoORGVwcmVjYXRlZEVu", + "dW0SEwoPREVQUkVDQVRFRF9aRVJPEAASBwoDb25lEAFCH0gBqgIaVW5pdFRl", + "c3QuSXNzdWVzLlRlc3RQcm90b3NiBnByb3RvMw==")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(new[] {typeof(global::UnitTest.Issues.TestProtos.NegativeEnum), typeof(global::UnitTest.Issues.TestProtos.DeprecatedEnum), }, new pbr::GeneratedClrTypeInfo[] { @@ -55,7 +57,8 @@ namespace UnitTest.Issues.TestProtos { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage), global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage.Parser, new[]{ "PrimitiveValue", "PrimitiveArray", "MessageValue", "MessageArray", "EnumValue", "EnumArray" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ItemField), global::UnitTest.Issues.TestProtos.ItemField.Parser, new[]{ "Item" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames), global::UnitTest.Issues.TestProtos.ReservedNames.Parser, new[]{ "Types_", "Descriptor_" }, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType), global::UnitTest.Issues.TestProtos.ReservedNames.Types.SomeNestedType.Parser, null, null, null, null)}), - new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering), global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering.Parser, new[]{ "PlainInt32", "O1String", "O1Int32", "PlainString", "O2Int32", "O2String" }, new[]{ "O1", "O2" }, null, null) + new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering), global::UnitTest.Issues.TestProtos.TestJsonFieldOrdering.Parser, new[]{ "PlainInt32", "O1String", "O1Int32", "PlainString", "O2Int32", "O2String" }, new[]{ "O1", "O2" }, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.TestJsonName), global::UnitTest.Issues.TestProtos.TestJsonName.Parser, new[]{ "Name", "Description", "Guid" }, null, null, null) })); } #endregion @@ -63,14 +66,14 @@ namespace UnitTest.Issues.TestProtos { } #region Enums public enum NegativeEnum { - NEGATIVE_ENUM_ZERO = 0, - FiveBelow = -5, - MinusOne = -1, + [pbr::OriginalName("NEGATIVE_ENUM_ZERO")] Zero = 0, + [pbr::OriginalName("FiveBelow")] FiveBelow = -5, + [pbr::OriginalName("MinusOne")] MinusOne = -1, } public enum DeprecatedEnum { - DEPRECATED_ZERO = 0, - one = 1, + [pbr::OriginalName("DEPRECATED_ZERO")] DeprecatedZero = 0, + [pbr::OriginalName("one")] One = 1, } #endregion @@ -353,7 +356,7 @@ namespace UnitTest.Issues.TestProtos { /// Field number for the "value" field. public const int ValueFieldNumber = 1; - private global::UnitTest.Issues.TestProtos.NegativeEnum value_ = global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO; + private global::UnitTest.Issues.TestProtos.NegativeEnum value_ = 0; public global::UnitTest.Issues.TestProtos.NegativeEnum Value { get { return value_; } set { @@ -398,7 +401,7 @@ namespace UnitTest.Issues.TestProtos { public override int GetHashCode() { int hash = 1; - if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO) hash ^= Value.GetHashCode(); + if (Value != 0) hash ^= Value.GetHashCode(); hash ^= values_.GetHashCode(); hash ^= packedValues_.GetHashCode(); return hash; @@ -409,7 +412,7 @@ namespace UnitTest.Issues.TestProtos { } public void WriteTo(pb::CodedOutputStream output) { - if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO) { + if (Value != 0) { output.WriteRawTag(8); output.WriteEnum((int) Value); } @@ -419,7 +422,7 @@ namespace UnitTest.Issues.TestProtos { public int CalculateSize() { int size = 0; - if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO) { + if (Value != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Value); } size += values_.CalculateSize(_repeated_values_codec); @@ -431,7 +434,7 @@ namespace UnitTest.Issues.TestProtos { if (other == null) { return; } - if (other.Value != global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO) { + if (other.Value != 0) { Value = other.Value; } values_.Add(other.values_); @@ -617,7 +620,7 @@ namespace UnitTest.Issues.TestProtos { /// Field number for the "EnumValue" field. public const int EnumValueFieldNumber = 5; - private global::UnitTest.Issues.TestProtos.DeprecatedEnum enumValue_ = global::UnitTest.Issues.TestProtos.DeprecatedEnum.DEPRECATED_ZERO; + private global::UnitTest.Issues.TestProtos.DeprecatedEnum enumValue_ = 0; [global::System.ObsoleteAttribute()] public global::UnitTest.Issues.TestProtos.DeprecatedEnum EnumValue { get { return enumValue_; } @@ -662,7 +665,7 @@ namespace UnitTest.Issues.TestProtos { hash ^= primitiveArray_.GetHashCode(); if (messageValue_ != null) hash ^= MessageValue.GetHashCode(); hash ^= messageArray_.GetHashCode(); - if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DEPRECATED_ZERO) hash ^= EnumValue.GetHashCode(); + if (EnumValue != 0) hash ^= EnumValue.GetHashCode(); hash ^= enumArray_.GetHashCode(); return hash; } @@ -682,7 +685,7 @@ namespace UnitTest.Issues.TestProtos { output.WriteMessage(MessageValue); } messageArray_.WriteTo(output, _repeated_messageArray_codec); - if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DEPRECATED_ZERO) { + if (EnumValue != 0) { output.WriteRawTag(40); output.WriteEnum((int) EnumValue); } @@ -699,7 +702,7 @@ namespace UnitTest.Issues.TestProtos { size += 1 + pb::CodedOutputStream.ComputeMessageSize(MessageValue); } size += messageArray_.CalculateSize(_repeated_messageArray_codec); - if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DEPRECATED_ZERO) { + if (EnumValue != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) EnumValue); } size += enumArray_.CalculateSize(_repeated_enumArray_codec); @@ -721,7 +724,7 @@ namespace UnitTest.Issues.TestProtos { MessageValue.MergeFrom(other.MessageValue); } messageArray_.Add(other.messageArray_); - if (other.EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DEPRECATED_ZERO) { + if (other.EnumValue != 0) { EnumValue = other.EnumValue; } enumArray_.Add(other.enumArray_); @@ -1399,6 +1402,166 @@ namespace UnitTest.Issues.TestProtos { } + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class TestJsonName : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new TestJsonName()); + public static pb::MessageParser Parser { get { return _parser; } } + + public static pbr::MessageDescriptor Descriptor { + get { return global::UnitTest.Issues.TestProtos.UnittestIssuesReflection.Descriptor.MessageTypes[7]; } + } + + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + public TestJsonName() { + OnConstruction(); + } + + partial void OnConstruction(); + + public TestJsonName(TestJsonName other) : this() { + name_ = other.name_; + description_ = other.description_; + guid_ = other.guid_; + } + + public TestJsonName Clone() { + return new TestJsonName(this); + } + + /// Field number for the "name" field. + public const int NameFieldNumber = 1; + private string name_ = ""; + /// + /// Message for testing the effects for of the json_name option + /// + public string Name { + get { return name_; } + set { + name_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "description" field. + public const int DescriptionFieldNumber = 2; + private string description_ = ""; + public string Description { + get { return description_; } + set { + description_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "guid" field. + public const int GuidFieldNumber = 3; + private string guid_ = ""; + public string Guid { + get { return guid_; } + set { + guid_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + public override bool Equals(object other) { + return Equals(other as TestJsonName); + } + + public bool Equals(TestJsonName other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Name != other.Name) return false; + if (Description != other.Description) return false; + if (Guid != other.Guid) return false; + return true; + } + + public override int GetHashCode() { + int hash = 1; + if (Name.Length != 0) hash ^= Name.GetHashCode(); + if (Description.Length != 0) hash ^= Description.GetHashCode(); + if (Guid.Length != 0) hash ^= Guid.GetHashCode(); + return hash; + } + + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + public void WriteTo(pb::CodedOutputStream output) { + if (Name.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Name); + } + if (Description.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Description); + } + if (Guid.Length != 0) { + output.WriteRawTag(26); + output.WriteString(Guid); + } + } + + public int CalculateSize() { + int size = 0; + if (Name.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Name); + } + if (Description.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Description); + } + if (Guid.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Guid); + } + return size; + } + + public void MergeFrom(TestJsonName other) { + if (other == null) { + return; + } + if (other.Name.Length != 0) { + Name = other.Name; + } + if (other.Description.Length != 0) { + Description = other.Description; + } + if (other.Guid.Length != 0) { + Guid = other.Guid; + } + } + + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + Name = input.ReadString(); + break; + } + case 18: { + Description = input.ReadString(); + break; + } + case 26: { + Guid = input.ReadString(); + break; + } + } + } + } + + } + #endregion } diff --git a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs index d84654488..b8d159bb5 100644 --- a/csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs +++ b/csharp/src/Google.Protobuf.Test/TestProtos/UnittestProto3.cs @@ -193,39 +193,39 @@ namespace Google.Protobuf.TestProtos { } #region Enums public enum ForeignEnum { - FOREIGN_UNSPECIFIED = 0, - FOREIGN_FOO = 4, - FOREIGN_BAR = 5, - FOREIGN_BAZ = 6, + [pbr::OriginalName("FOREIGN_UNSPECIFIED")] ForeignUnspecified = 0, + [pbr::OriginalName("FOREIGN_FOO")] ForeignFoo = 4, + [pbr::OriginalName("FOREIGN_BAR")] ForeignBar = 5, + [pbr::OriginalName("FOREIGN_BAZ")] ForeignBaz = 6, } /// /// Test an enum that has multiple values with the same number. /// public enum TestEnumWithDupValue { - TEST_ENUM_WITH_DUP_VALUE_UNSPECIFIED = 0, - FOO1 = 1, - BAR1 = 2, - BAZ = 3, - FOO2 = 1, - BAR2 = 2, + [pbr::OriginalName("TEST_ENUM_WITH_DUP_VALUE_UNSPECIFIED")] Unspecified = 0, + [pbr::OriginalName("FOO1")] Foo1 = 1, + [pbr::OriginalName("BAR1")] Bar1 = 2, + [pbr::OriginalName("BAZ")] Baz = 3, + [pbr::OriginalName("FOO2")] Foo2 = 1, + [pbr::OriginalName("BAR2")] Bar2 = 2, } /// /// Test an enum with large, unordered values. /// public enum TestSparseEnum { - TEST_SPARSE_ENUM_UNSPECIFIED = 0, - SPARSE_A = 123, - SPARSE_B = 62374, - SPARSE_C = 12589234, - SPARSE_D = -15, - SPARSE_E = -53452, + [pbr::OriginalName("TEST_SPARSE_ENUM_UNSPECIFIED")] Unspecified = 0, + [pbr::OriginalName("SPARSE_A")] SparseA = 123, + [pbr::OriginalName("SPARSE_B")] SparseB = 62374, + [pbr::OriginalName("SPARSE_C")] SparseC = 12589234, + [pbr::OriginalName("SPARSE_D")] SparseD = -15, + [pbr::OriginalName("SPARSE_E")] SparseE = -53452, /// /// In proto3, value 0 must be the first one specified /// SPARSE_F = 0; /// - SPARSE_G = 2, + [pbr::OriginalName("SPARSE_G")] SparseG = 2, } #endregion @@ -505,7 +505,7 @@ namespace Google.Protobuf.TestProtos { /// Field number for the "single_nested_enum" field. public const int SingleNestedEnumFieldNumber = 21; - private global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum singleNestedEnum_ = global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum.NESTED_ENUM_UNSPECIFIED; + private global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum singleNestedEnum_ = 0; public global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum SingleNestedEnum { get { return singleNestedEnum_; } set { @@ -515,7 +515,7 @@ namespace Google.Protobuf.TestProtos { /// Field number for the "single_foreign_enum" field. public const int SingleForeignEnumFieldNumber = 22; - private global::Google.Protobuf.TestProtos.ForeignEnum singleForeignEnum_ = global::Google.Protobuf.TestProtos.ForeignEnum.FOREIGN_UNSPECIFIED; + private global::Google.Protobuf.TestProtos.ForeignEnum singleForeignEnum_ = 0; public global::Google.Protobuf.TestProtos.ForeignEnum SingleForeignEnum { get { return singleForeignEnum_; } set { @@ -525,7 +525,7 @@ namespace Google.Protobuf.TestProtos { /// Field number for the "single_import_enum" field. public const int SingleImportEnumFieldNumber = 23; - private global::Google.Protobuf.TestProtos.ImportEnum singleImportEnum_ = global::Google.Protobuf.TestProtos.ImportEnum.IMPORT_ENUM_UNSPECIFIED; + private global::Google.Protobuf.TestProtos.ImportEnum singleImportEnum_ = 0; public global::Google.Protobuf.TestProtos.ImportEnum SingleImportEnum { get { return singleImportEnum_; } set { @@ -892,9 +892,9 @@ namespace Google.Protobuf.TestProtos { if (singleNestedMessage_ != null) hash ^= SingleNestedMessage.GetHashCode(); if (singleForeignMessage_ != null) hash ^= SingleForeignMessage.GetHashCode(); if (singleImportMessage_ != null) hash ^= SingleImportMessage.GetHashCode(); - if (SingleNestedEnum != global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum.NESTED_ENUM_UNSPECIFIED) hash ^= SingleNestedEnum.GetHashCode(); - if (SingleForeignEnum != global::Google.Protobuf.TestProtos.ForeignEnum.FOREIGN_UNSPECIFIED) hash ^= SingleForeignEnum.GetHashCode(); - if (SingleImportEnum != global::Google.Protobuf.TestProtos.ImportEnum.IMPORT_ENUM_UNSPECIFIED) hash ^= SingleImportEnum.GetHashCode(); + if (SingleNestedEnum != 0) hash ^= SingleNestedEnum.GetHashCode(); + if (SingleForeignEnum != 0) hash ^= SingleForeignEnum.GetHashCode(); + if (SingleImportEnum != 0) hash ^= SingleImportEnum.GetHashCode(); if (singlePublicImportMessage_ != null) hash ^= SinglePublicImportMessage.GetHashCode(); hash ^= repeatedInt32_.GetHashCode(); hash ^= repeatedInt64_.GetHashCode(); @@ -1003,15 +1003,15 @@ namespace Google.Protobuf.TestProtos { output.WriteRawTag(162, 1); output.WriteMessage(SingleImportMessage); } - if (SingleNestedEnum != global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum.NESTED_ENUM_UNSPECIFIED) { + if (SingleNestedEnum != 0) { output.WriteRawTag(168, 1); output.WriteEnum((int) SingleNestedEnum); } - if (SingleForeignEnum != global::Google.Protobuf.TestProtos.ForeignEnum.FOREIGN_UNSPECIFIED) { + if (SingleForeignEnum != 0) { output.WriteRawTag(176, 1); output.WriteEnum((int) SingleForeignEnum); } - if (SingleImportEnum != global::Google.Protobuf.TestProtos.ImportEnum.IMPORT_ENUM_UNSPECIFIED) { + if (SingleImportEnum != 0) { output.WriteRawTag(184, 1); output.WriteEnum((int) SingleImportEnum); } @@ -1115,13 +1115,13 @@ namespace Google.Protobuf.TestProtos { if (singleImportMessage_ != null) { size += 2 + pb::CodedOutputStream.ComputeMessageSize(SingleImportMessage); } - if (SingleNestedEnum != global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum.NESTED_ENUM_UNSPECIFIED) { + if (SingleNestedEnum != 0) { size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) SingleNestedEnum); } - if (SingleForeignEnum != global::Google.Protobuf.TestProtos.ForeignEnum.FOREIGN_UNSPECIFIED) { + if (SingleForeignEnum != 0) { size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) SingleForeignEnum); } - if (SingleImportEnum != global::Google.Protobuf.TestProtos.ImportEnum.IMPORT_ENUM_UNSPECIFIED) { + if (SingleImportEnum != 0) { size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) SingleImportEnum); } if (singlePublicImportMessage_ != null) { @@ -1231,13 +1231,13 @@ namespace Google.Protobuf.TestProtos { } SingleImportMessage.MergeFrom(other.SingleImportMessage); } - if (other.SingleNestedEnum != global::Google.Protobuf.TestProtos.TestAllTypes.Types.NestedEnum.NESTED_ENUM_UNSPECIFIED) { + if (other.SingleNestedEnum != 0) { SingleNestedEnum = other.SingleNestedEnum; } - if (other.SingleForeignEnum != global::Google.Protobuf.TestProtos.ForeignEnum.FOREIGN_UNSPECIFIED) { + if (other.SingleForeignEnum != 0) { SingleForeignEnum = other.SingleForeignEnum; } - if (other.SingleImportEnum != global::Google.Protobuf.TestProtos.ImportEnum.IMPORT_ENUM_UNSPECIFIED) { + if (other.SingleImportEnum != 0) { SingleImportEnum = other.SingleImportEnum; } if (other.singlePublicImportMessage_ != null) { @@ -1526,14 +1526,14 @@ namespace Google.Protobuf.TestProtos { [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] public static partial class Types { public enum NestedEnum { - NESTED_ENUM_UNSPECIFIED = 0, - FOO = 1, - BAR = 2, - BAZ = 3, + [pbr::OriginalName("NESTED_ENUM_UNSPECIFIED")] Unspecified = 0, + [pbr::OriginalName("FOO")] Foo = 1, + [pbr::OriginalName("BAR")] Bar = 2, + [pbr::OriginalName("BAZ")] Baz = 3, /// /// Intentionally negative. /// - NEG = -1, + [pbr::OriginalName("NEG")] Neg = -1, } [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] @@ -2793,7 +2793,7 @@ namespace Google.Protobuf.TestProtos { /// Field number for the "EnumField" field. public const int EnumFieldFieldNumber = 3; - private global::Google.Protobuf.TestProtos.ForeignEnum enumField_ = global::Google.Protobuf.TestProtos.ForeignEnum.FOREIGN_UNSPECIFIED; + private global::Google.Protobuf.TestProtos.ForeignEnum enumField_ = 0; public global::Google.Protobuf.TestProtos.ForeignEnum EnumField { get { return enumField_; } set { @@ -2873,7 +2873,7 @@ namespace Google.Protobuf.TestProtos { int hash = 1; if (PrimitiveField != 0) hash ^= PrimitiveField.GetHashCode(); if (StringField.Length != 0) hash ^= StringField.GetHashCode(); - if (EnumField != global::Google.Protobuf.TestProtos.ForeignEnum.FOREIGN_UNSPECIFIED) hash ^= EnumField.GetHashCode(); + if (EnumField != 0) hash ^= EnumField.GetHashCode(); if (messageField_ != null) hash ^= MessageField.GetHashCode(); hash ^= repeatedPrimitiveField_.GetHashCode(); hash ^= repeatedStringField_.GetHashCode(); @@ -2895,7 +2895,7 @@ namespace Google.Protobuf.TestProtos { output.WriteRawTag(18); output.WriteString(StringField); } - if (EnumField != global::Google.Protobuf.TestProtos.ForeignEnum.FOREIGN_UNSPECIFIED) { + if (EnumField != 0) { output.WriteRawTag(24); output.WriteEnum((int) EnumField); } @@ -2917,7 +2917,7 @@ namespace Google.Protobuf.TestProtos { if (StringField.Length != 0) { size += 1 + pb::CodedOutputStream.ComputeStringSize(StringField); } - if (EnumField != global::Google.Protobuf.TestProtos.ForeignEnum.FOREIGN_UNSPECIFIED) { + if (EnumField != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) EnumField); } if (messageField_ != null) { @@ -2940,7 +2940,7 @@ namespace Google.Protobuf.TestProtos { if (other.StringField.Length != 0) { StringField = other.StringField; } - if (other.EnumField != global::Google.Protobuf.TestProtos.ForeignEnum.FOREIGN_UNSPECIFIED) { + if (other.EnumField != 0) { EnumField = other.EnumField; } if (other.messageField_ != null) { @@ -3370,7 +3370,7 @@ namespace Google.Protobuf.TestProtos { /// Field number for the "sparse_enum" field. public const int SparseEnumFieldNumber = 1; - private global::Google.Protobuf.TestProtos.TestSparseEnum sparseEnum_ = global::Google.Protobuf.TestProtos.TestSparseEnum.TEST_SPARSE_ENUM_UNSPECIFIED; + private global::Google.Protobuf.TestProtos.TestSparseEnum sparseEnum_ = 0; public global::Google.Protobuf.TestProtos.TestSparseEnum SparseEnum { get { return sparseEnum_; } set { @@ -3395,7 +3395,7 @@ namespace Google.Protobuf.TestProtos { public override int GetHashCode() { int hash = 1; - if (SparseEnum != global::Google.Protobuf.TestProtos.TestSparseEnum.TEST_SPARSE_ENUM_UNSPECIFIED) hash ^= SparseEnum.GetHashCode(); + if (SparseEnum != 0) hash ^= SparseEnum.GetHashCode(); return hash; } @@ -3404,7 +3404,7 @@ namespace Google.Protobuf.TestProtos { } public void WriteTo(pb::CodedOutputStream output) { - if (SparseEnum != global::Google.Protobuf.TestProtos.TestSparseEnum.TEST_SPARSE_ENUM_UNSPECIFIED) { + if (SparseEnum != 0) { output.WriteRawTag(8); output.WriteEnum((int) SparseEnum); } @@ -3412,7 +3412,7 @@ namespace Google.Protobuf.TestProtos { public int CalculateSize() { int size = 0; - if (SparseEnum != global::Google.Protobuf.TestProtos.TestSparseEnum.TEST_SPARSE_ENUM_UNSPECIFIED) { + if (SparseEnum != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) SparseEnum); } return size; @@ -3422,7 +3422,7 @@ namespace Google.Protobuf.TestProtos { if (other == null) { return; } - if (other.SparseEnum != global::Google.Protobuf.TestProtos.TestSparseEnum.TEST_SPARSE_ENUM_UNSPECIFIED) { + if (other.SparseEnum != 0) { SparseEnum = other.SparseEnum; } } diff --git a/csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs b/csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs index f3593e5fa..f21be7d9b 100644 --- a/csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs +++ b/csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs @@ -46,6 +46,24 @@ namespace Google.Protobuf.WellKnownTypes Assert.AreEqual(message.CalculateSize(), any.Value.Length); } + [Test] + public void Pack_WithCustomPrefix() + { + var message = SampleMessages.CreateFullTestAllTypes(); + var any = Any.Pack(message, "foo.bar/baz"); + Assert.AreEqual("foo.bar/baz/protobuf_unittest.TestAllTypes", any.TypeUrl); + Assert.AreEqual(message.CalculateSize(), any.Value.Length); + } + + [Test] + public void Pack_WithCustomPrefixTrailingSlash() + { + var message = SampleMessages.CreateFullTestAllTypes(); + var any = Any.Pack(message, "foo.bar/baz/"); + Assert.AreEqual("foo.bar/baz/protobuf_unittest.TestAllTypes", any.TypeUrl); + Assert.AreEqual(message.CalculateSize(), any.Value.Length); + } + [Test] public void Unpack_WrongType() { @@ -63,6 +81,15 @@ namespace Google.Protobuf.WellKnownTypes Assert.AreEqual(message, unpacked); } + [Test] + public void Unpack_CustomPrefix_Success() + { + var message = SampleMessages.CreateFullTestAllTypes(); + var any = Any.Pack(message, "foo.bar/baz"); + var unpacked = any.Unpack(); + Assert.AreEqual(message, unpacked); + } + [Test] public void ToString_WithValues() { diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.csproj b/csharp/src/Google.Protobuf/Google.Protobuf.csproj index ef524bafa..5557612a8 100644 --- a/csharp/src/Google.Protobuf/Google.Protobuf.csproj +++ b/csharp/src/Google.Protobuf/Google.Protobuf.csproj @@ -117,6 +117,7 @@ + diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.nuspec b/csharp/src/Google.Protobuf/Google.Protobuf.nuspec index d5302544c..2892b8bf7 100644 --- a/csharp/src/Google.Protobuf/Google.Protobuf.nuspec +++ b/csharp/src/Google.Protobuf/Google.Protobuf.nuspec @@ -23,7 +23,6 @@ - @@ -34,10 +33,12 @@ + + @@ -49,18 +50,5 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/JsonFormatter.cs b/csharp/src/Google.Protobuf/JsonFormatter.cs index eb959c9f0..837724732 100644 --- a/csharp/src/Google.Protobuf/JsonFormatter.cs +++ b/csharp/src/Google.Protobuf/JsonFormatter.cs @@ -39,6 +39,7 @@ using Google.Protobuf.WellKnownTypes; using System.IO; using System.Linq; using System.Collections.Generic; +using System.Reflection; namespace Google.Protobuf { @@ -237,11 +238,13 @@ namespace Google.Protobuf { writer.Write(PropertySeparator); } - WriteString(writer, ToCamelCase(accessor.Descriptor.Name)); + + WriteString(writer, accessor.Descriptor.JsonName); writer.Write(NameValueSeparator); WriteValue(writer, value); + first = false; - } + } return !first; } @@ -418,9 +421,10 @@ namespace Google.Protobuf } else if (value is System.Enum) { - if (System.Enum.IsDefined(value.GetType(), value)) + string name = OriginalEnumValueHelper.GetOriginalName(value); + if (name != null) { - WriteString(writer, value.ToString()); + WriteString(writer, name); } else { @@ -563,7 +567,7 @@ namespace Google.Protobuf string typeUrl = (string) value.Descriptor.Fields[Any.TypeUrlFieldNumber].Accessor.GetValue(value); ByteString data = (ByteString) value.Descriptor.Fields[Any.ValueFieldNumber].Accessor.GetValue(value); - string typeName = GetTypeName(typeUrl); + string typeName = Any.GetTypeName(typeUrl); MessageDescriptor descriptor = settings.TypeRegistry.Find(typeName); if (descriptor == null) { @@ -604,17 +608,7 @@ namespace Google.Protobuf writer.Write(data.ToBase64()); writer.Write('"'); writer.Write(" }"); - } - - internal static string GetTypeName(String typeUrl) - { - string[] parts = typeUrl.Split('/'); - if (parts.Length != 2 || parts[0] != TypeUrlPrefix) - { - throw new InvalidProtocolBufferException($"Invalid type url: {typeUrl}"); - } - return parts[1]; - } + } private void WriteStruct(TextWriter writer, IMessage message) { @@ -875,5 +869,44 @@ namespace Google.Protobuf TypeRegistry = ProtoPreconditions.CheckNotNull(typeRegistry, nameof(typeRegistry)); } } + + // Effectively a cache of mapping from enum values to the original name as specified in the proto file, + // fetched by reflection. + // The need for this is unfortunate, as is its unbounded size, but realistically it shouldn't cause issues. + private static class OriginalEnumValueHelper + { + // TODO: In the future we might want to use ConcurrentDictionary, at the point where all + // the platforms we target have it. + private static readonly Dictionary> dictionaries + = new Dictionary>(); + + internal static string GetOriginalName(object value) + { + var enumType = value.GetType(); + Dictionary nameMapping; + lock (dictionaries) + { + if (!dictionaries.TryGetValue(enumType, out nameMapping)) + { + nameMapping = GetNameMapping(enumType); + dictionaries[enumType] = nameMapping; + } + } + + string originalName; + // If this returns false, originalName will be null, which is what we want. + nameMapping.TryGetValue(value, out originalName); + return originalName; + } + + private static Dictionary GetNameMapping(System.Type enumType) => + enumType.GetTypeInfo().DeclaredFields + .Where(f => f.IsStatic) + .ToDictionary(f => f.GetValue(null), + f => f.GetCustomAttributes() + .FirstOrDefault() + // If the attribute hasn't been applied, fall back to the name of the field. + ?.Name ?? f.Name); + } } } diff --git a/csharp/src/Google.Protobuf/JsonParser.cs b/csharp/src/Google.Protobuf/JsonParser.cs index 80d3013da..d738ebb04 100644 --- a/csharp/src/Google.Protobuf/JsonParser.cs +++ b/csharp/src/Google.Protobuf/JsonParser.cs @@ -513,7 +513,7 @@ namespace Google.Protobuf throw new InvalidProtocolBufferException("Expected string value for Any.@type"); } string typeUrl = token.StringValue; - string typeName = JsonFormatter.GetTypeName(typeUrl); + string typeName = Any.GetTypeName(typeUrl); MessageDescriptor descriptor = settings.TypeRegistry.Find(typeName); if (descriptor == null) diff --git a/csharp/src/Google.Protobuf/Reflection/Descriptor.cs b/csharp/src/Google.Protobuf/Reflection/Descriptor.cs index 7de7b5fc6..fa138dfe9 100644 --- a/csharp/src/Google.Protobuf/Reflection/Descriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/Descriptor.cs @@ -80,7 +80,7 @@ namespace Google.Protobuf.Reflection { "EhMKC291dHB1dF90eXBlGAMgASgJEi8KB29wdGlvbnMYBCABKAsyHi5nb29n", "bGUucHJvdG9idWYuTWV0aG9kT3B0aW9ucxIfChBjbGllbnRfc3RyZWFtaW5n", "GAUgASgIOgVmYWxzZRIfChBzZXJ2ZXJfc3RyZWFtaW5nGAYgASgIOgVmYWxz", - "ZSKuBQoLRmlsZU9wdGlvbnMSFAoMamF2YV9wYWNrYWdlGAEgASgJEhwKFGph", + "ZSKHBQoLRmlsZU9wdGlvbnMSFAoMamF2YV9wYWNrYWdlGAEgASgJEhwKFGph", "dmFfb3V0ZXJfY2xhc3NuYW1lGAggASgJEiIKE2phdmFfbXVsdGlwbGVfZmls", "ZXMYCiABKAg6BWZhbHNlEiwKHWphdmFfZ2VuZXJhdGVfZXF1YWxzX2FuZF9o", "YXNoGBQgASgIOgVmYWxzZRIlChZqYXZhX3N0cmluZ19jaGVja191dGY4GBsg", @@ -91,54 +91,53 @@ namespace Google.Protobuf.Reflection { "cHlfZ2VuZXJpY19zZXJ2aWNlcxgSIAEoCDoFZmFsc2USGQoKZGVwcmVjYXRl", "ZBgXIAEoCDoFZmFsc2USHwoQY2NfZW5hYmxlX2FyZW5hcxgfIAEoCDoFZmFs", "c2USGQoRb2JqY19jbGFzc19wcmVmaXgYJCABKAkSGAoQY3NoYXJwX25hbWVz", - "cGFjZRglIAEoCRIrCh9qYXZhbmFub191c2VfZGVwcmVjYXRlZF9wYWNrYWdl", - "GCYgASgIQgIYARJDChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5n", + "cGFjZRglIAEoCRJDChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5n", "b29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbiI6CgxPcHRpbWl6", "ZU1vZGUSCQoFU1BFRUQQARINCglDT0RFX1NJWkUQAhIQCgxMSVRFX1JVTlRJ", - "TUUQAyoJCOgHEICAgIACIuYBCg5NZXNzYWdlT3B0aW9ucxImChdtZXNzYWdl", - "X3NldF93aXJlX2Zvcm1hdBgBIAEoCDoFZmFsc2USLgofbm9fc3RhbmRhcmRf", - "ZGVzY3JpcHRvcl9hY2Nlc3NvchgCIAEoCDoFZmFsc2USGQoKZGVwcmVjYXRl", - "ZBgDIAEoCDoFZmFsc2USEQoJbWFwX2VudHJ5GAcgASgIEkMKFHVuaW50ZXJw", - "cmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVy", - "cHJldGVkT3B0aW9uKgkI6AcQgICAgAIimAMKDEZpZWxkT3B0aW9ucxI6CgVj", - "dHlwZRgBIAEoDjIjLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnMuQ1R5", - "cGU6BlNUUklORxIOCgZwYWNrZWQYAiABKAgSPwoGanN0eXBlGAYgASgOMiQu", - "Z29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucy5KU1R5cGU6CUpTX05PUk1B", - "TBITCgRsYXp5GAUgASgIOgVmYWxzZRIZCgpkZXByZWNhdGVkGAMgASgIOgVm", - "YWxzZRITCgR3ZWFrGAogASgIOgVmYWxzZRJDChR1bmludGVycHJldGVkX29w", + "TUUQAyoJCOgHEICAgIACSgQIJhAnIuYBCg5NZXNzYWdlT3B0aW9ucxImChdt", + "ZXNzYWdlX3NldF93aXJlX2Zvcm1hdBgBIAEoCDoFZmFsc2USLgofbm9fc3Rh", + "bmRhcmRfZGVzY3JpcHRvcl9hY2Nlc3NvchgCIAEoCDoFZmFsc2USGQoKZGVw", + "cmVjYXRlZBgDIAEoCDoFZmFsc2USEQoJbWFwX2VudHJ5GAcgASgIEkMKFHVu", + "aW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5V", + "bmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIimAMKDEZpZWxkT3B0aW9u", + "cxI6CgVjdHlwZRgBIAEoDjIjLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlv", + "bnMuQ1R5cGU6BlNUUklORxIOCgZwYWNrZWQYAiABKAgSPwoGanN0eXBlGAYg", + "ASgOMiQuZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucy5KU1R5cGU6CUpT", + "X05PUk1BTBITCgRsYXp5GAUgASgIOgVmYWxzZRIZCgpkZXByZWNhdGVkGAMg", + "ASgIOgVmYWxzZRITCgR3ZWFrGAogASgIOgVmYWxzZRJDChR1bmludGVycHJl", + "dGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnBy", + "ZXRlZE9wdGlvbiIvCgVDVHlwZRIKCgZTVFJJTkcQABIICgRDT1JEEAESEAoM", + "U1RSSU5HX1BJRUNFEAIiNQoGSlNUeXBlEg0KCUpTX05PUk1BTBAAEg0KCUpT", + "X1NUUklORxABEg0KCUpTX05VTUJFUhACKgkI6AcQgICAgAIijQEKC0VudW1P", + "cHRpb25zEhMKC2FsbG93X2FsaWFzGAIgASgIEhkKCmRlcHJlY2F0ZWQYAyAB", + "KAg6BWZhbHNlEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdv", + "b2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIi", + "fQoQRW51bVZhbHVlT3B0aW9ucxIZCgpkZXByZWNhdGVkGAEgASgIOgVmYWxz", + "ZRJDChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJv", + "dG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbioJCOgHEICAgIACInsKDlNlcnZp", + "Y2VPcHRpb25zEhkKCmRlcHJlY2F0ZWQYISABKAg6BWZhbHNlEkMKFHVuaW50", + "ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5Vbmlu", + "dGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIiegoNTWV0aG9kT3B0aW9ucxIZ", + "CgpkZXByZWNhdGVkGCEgASgIOgVmYWxzZRJDChR1bmludGVycHJldGVkX29w", "dGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9w", - "dGlvbiIvCgVDVHlwZRIKCgZTVFJJTkcQABIICgRDT1JEEAESEAoMU1RSSU5H", - "X1BJRUNFEAIiNQoGSlNUeXBlEg0KCUpTX05PUk1BTBAAEg0KCUpTX1NUUklO", - "RxABEg0KCUpTX05VTUJFUhACKgkI6AcQgICAgAIijQEKC0VudW1PcHRpb25z", - "EhMKC2FsbG93X2FsaWFzGAIgASgIEhkKCmRlcHJlY2F0ZWQYAyABKAg6BWZh", - "bHNlEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5w", - "cm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIifQoQRW51", - "bVZhbHVlT3B0aW9ucxIZCgpkZXByZWNhdGVkGAEgASgIOgVmYWxzZRJDChR1", - "bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYu", - "VW5pbnRlcnByZXRlZE9wdGlvbioJCOgHEICAgIACInsKDlNlcnZpY2VPcHRp", - "b25zEhkKCmRlcHJlY2F0ZWQYISABKAg6BWZhbHNlEkMKFHVuaW50ZXJwcmV0", - "ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJl", - "dGVkT3B0aW9uKgkI6AcQgICAgAIiegoNTWV0aG9kT3B0aW9ucxIZCgpkZXBy", - "ZWNhdGVkGCEgASgIOgVmYWxzZRJDChR1bmludGVycHJldGVkX29wdGlvbhjn", - "ByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbioJ", - "COgHEICAgIACIp4CChNVbmludGVycHJldGVkT3B0aW9uEjsKBG5hbWUYAiAD", - "KAsyLS5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbi5OYW1l", - "UGFydBIYChBpZGVudGlmaWVyX3ZhbHVlGAMgASgJEhoKEnBvc2l0aXZlX2lu", - "dF92YWx1ZRgEIAEoBBIaChJuZWdhdGl2ZV9pbnRfdmFsdWUYBSABKAMSFAoM", - "ZG91YmxlX3ZhbHVlGAYgASgBEhQKDHN0cmluZ192YWx1ZRgHIAEoDBIXCg9h", - "Z2dyZWdhdGVfdmFsdWUYCCABKAkaMwoITmFtZVBhcnQSEQoJbmFtZV9wYXJ0", - "GAEgAigJEhQKDGlzX2V4dGVuc2lvbhgCIAIoCCLVAQoOU291cmNlQ29kZUlu", - "Zm8SOgoIbG9jYXRpb24YASADKAsyKC5nb29nbGUucHJvdG9idWYuU291cmNl", - "Q29kZUluZm8uTG9jYXRpb24ahgEKCExvY2F0aW9uEhAKBHBhdGgYASADKAVC", - "AhABEhAKBHNwYW4YAiADKAVCAhABEhgKEGxlYWRpbmdfY29tbWVudHMYAyAB", - "KAkSGQoRdHJhaWxpbmdfY29tbWVudHMYBCABKAkSIQoZbGVhZGluZ19kZXRh", - "Y2hlZF9jb21tZW50cxgGIAMoCSKnAQoRR2VuZXJhdGVkQ29kZUluZm8SQQoK", - "YW5ub3RhdGlvbhgBIAMoCzItLmdvb2dsZS5wcm90b2J1Zi5HZW5lcmF0ZWRD", - "b2RlSW5mby5Bbm5vdGF0aW9uGk8KCkFubm90YXRpb24SEAoEcGF0aBgBIAMo", - "BUICEAESEwoLc291cmNlX2ZpbGUYAiABKAkSDQoFYmVnaW4YAyABKAUSCwoD", - "ZW5kGAQgASgFQlgKE2NvbS5nb29nbGUucHJvdG9idWZCEERlc2NyaXB0b3JQ", - "cm90b3NIAVoKZGVzY3JpcHRvcqICA0dQQqoCGkdvb2dsZS5Qcm90b2J1Zi5S", - "ZWZsZWN0aW9u")); + "dGlvbioJCOgHEICAgIACIp4CChNVbmludGVycHJldGVkT3B0aW9uEjsKBG5h", + "bWUYAiADKAsyLS5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlv", + "bi5OYW1lUGFydBIYChBpZGVudGlmaWVyX3ZhbHVlGAMgASgJEhoKEnBvc2l0", + "aXZlX2ludF92YWx1ZRgEIAEoBBIaChJuZWdhdGl2ZV9pbnRfdmFsdWUYBSAB", + "KAMSFAoMZG91YmxlX3ZhbHVlGAYgASgBEhQKDHN0cmluZ192YWx1ZRgHIAEo", + "DBIXCg9hZ2dyZWdhdGVfdmFsdWUYCCABKAkaMwoITmFtZVBhcnQSEQoJbmFt", + "ZV9wYXJ0GAEgAigJEhQKDGlzX2V4dGVuc2lvbhgCIAIoCCLVAQoOU291cmNl", + "Q29kZUluZm8SOgoIbG9jYXRpb24YASADKAsyKC5nb29nbGUucHJvdG9idWYu", + "U291cmNlQ29kZUluZm8uTG9jYXRpb24ahgEKCExvY2F0aW9uEhAKBHBhdGgY", + "ASADKAVCAhABEhAKBHNwYW4YAiADKAVCAhABEhgKEGxlYWRpbmdfY29tbWVu", + "dHMYAyABKAkSGQoRdHJhaWxpbmdfY29tbWVudHMYBCABKAkSIQoZbGVhZGlu", + "Z19kZXRhY2hlZF9jb21tZW50cxgGIAMoCSKnAQoRR2VuZXJhdGVkQ29kZUlu", + "Zm8SQQoKYW5ub3RhdGlvbhgBIAMoCzItLmdvb2dsZS5wcm90b2J1Zi5HZW5l", + "cmF0ZWRDb2RlSW5mby5Bbm5vdGF0aW9uGk8KCkFubm90YXRpb24SEAoEcGF0", + "aBgBIAMoBUICEAESEwoLc291cmNlX2ZpbGUYAiABKAkSDQoFYmVnaW4YAyAB", + "KAUSCwoDZW5kGAQgASgFQlgKE2NvbS5nb29nbGUucHJvdG9idWZCEERlc2Ny", + "aXB0b3JQcm90b3NIAVoKZGVzY3JpcHRvcqICA0dQQqoCGkdvb2dsZS5Qcm90", + "b2J1Zi5SZWZsZWN0aW9u")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { @@ -152,7 +151,7 @@ namespace Google.Protobuf.Reflection { new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumValueDescriptorProto), global::Google.Protobuf.Reflection.EnumValueDescriptorProto.Parser, new[]{ "Name", "Number", "Options" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.ServiceDescriptorProto), global::Google.Protobuf.Reflection.ServiceDescriptorProto.Parser, new[]{ "Name", "Method", "Options" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.MethodDescriptorProto), global::Google.Protobuf.Reflection.MethodDescriptorProto.Parser, new[]{ "Name", "InputType", "OutputType", "Options", "ClientStreaming", "ServerStreaming" }, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileOptions), global::Google.Protobuf.Reflection.FileOptions.Parser, new[]{ "JavaPackage", "JavaOuterClassname", "JavaMultipleFiles", "JavaGenerateEqualsAndHash", "JavaStringCheckUtf8", "OptimizeFor", "GoPackage", "CcGenericServices", "JavaGenericServices", "PyGenericServices", "Deprecated", "CcEnableArenas", "ObjcClassPrefix", "CsharpNamespace", "JavananoUseDeprecatedPackage", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode) }, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FileOptions), global::Google.Protobuf.Reflection.FileOptions.Parser, new[]{ "JavaPackage", "JavaOuterClassname", "JavaMultipleFiles", "JavaGenerateEqualsAndHash", "JavaStringCheckUtf8", "OptimizeFor", "GoPackage", "CcGenericServices", "JavaGenericServices", "PyGenericServices", "Deprecated", "CcEnableArenas", "ObjcClassPrefix", "CsharpNamespace", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode) }, null), new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.MessageOptions), global::Google.Protobuf.Reflection.MessageOptions.Parser, new[]{ "MessageSetWireFormat", "NoStandardDescriptorAccessor", "Deprecated", "MapEntry", "UninterpretedOption" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.FieldOptions), global::Google.Protobuf.Reflection.FieldOptions.Parser, new[]{ "Ctype", "Packed", "Jstype", "Lazy", "Deprecated", "Weak", "UninterpretedOption" }, null, new[]{ typeof(global::Google.Protobuf.Reflection.FieldOptions.Types.CType), typeof(global::Google.Protobuf.Reflection.FieldOptions.Types.JSType) }, null), new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.Reflection.EnumOptions), global::Google.Protobuf.Reflection.EnumOptions.Parser, new[]{ "AllowAlias", "Deprecated", "UninterpretedOption" }, null, null, null), @@ -1291,7 +1290,7 @@ namespace Google.Protobuf.Reflection { /// Field number for the "label" field. public const int LabelFieldNumber = 4; - private global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label label_ = global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label.LABEL_OPTIONAL; + private global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label label_ = 0; public global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label Label { get { return label_; } set { @@ -1301,7 +1300,7 @@ namespace Google.Protobuf.Reflection { /// Field number for the "type" field. public const int TypeFieldNumber = 5; - private global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Type type_ = global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Type.TYPE_DOUBLE; + private global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Type type_ = 0; /// /// If type_name is set, this need not be set. If both this and type_name /// are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. @@ -1429,8 +1428,8 @@ namespace Google.Protobuf.Reflection { int hash = 1; if (Name.Length != 0) hash ^= Name.GetHashCode(); if (Number != 0) hash ^= Number.GetHashCode(); - if (Label != global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label.LABEL_OPTIONAL) hash ^= Label.GetHashCode(); - if (Type != global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Type.TYPE_DOUBLE) hash ^= Type.GetHashCode(); + if (Label != 0) hash ^= Label.GetHashCode(); + if (Type != 0) hash ^= Type.GetHashCode(); if (TypeName.Length != 0) hash ^= TypeName.GetHashCode(); if (Extendee.Length != 0) hash ^= Extendee.GetHashCode(); if (DefaultValue.Length != 0) hash ^= DefaultValue.GetHashCode(); @@ -1457,11 +1456,11 @@ namespace Google.Protobuf.Reflection { output.WriteRawTag(24); output.WriteInt32(Number); } - if (Label != global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label.LABEL_OPTIONAL) { + if (Label != 0) { output.WriteRawTag(32); output.WriteEnum((int) Label); } - if (Type != global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Type.TYPE_DOUBLE) { + if (Type != 0) { output.WriteRawTag(40); output.WriteEnum((int) Type); } @@ -1495,10 +1494,10 @@ namespace Google.Protobuf.Reflection { if (Number != 0) { size += 1 + pb::CodedOutputStream.ComputeInt32Size(Number); } - if (Label != global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label.LABEL_OPTIONAL) { + if (Label != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Label); } - if (Type != global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Type.TYPE_DOUBLE) { + if (Type != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Type); } if (TypeName.Length != 0) { @@ -1532,10 +1531,10 @@ namespace Google.Protobuf.Reflection { if (other.Number != 0) { Number = other.Number; } - if (other.Label != global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Label.LABEL_OPTIONAL) { + if (other.Label != 0) { Label = other.Label; } - if (other.Type != global::Google.Protobuf.Reflection.FieldDescriptorProto.Types.Type.TYPE_DOUBLE) { + if (other.Type != 0) { Type = other.Type; } if (other.TypeName.Length != 0) { @@ -1624,59 +1623,59 @@ namespace Google.Protobuf.Reflection { /// 0 is reserved for errors. /// Order is weird for historical reasons. /// - TYPE_DOUBLE = 1, - TYPE_FLOAT = 2, + [pbr::OriginalName("TYPE_DOUBLE")] Double = 1, + [pbr::OriginalName("TYPE_FLOAT")] Float = 2, /// /// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if /// negative values are likely. /// - TYPE_INT64 = 3, - TYPE_UINT64 = 4, + [pbr::OriginalName("TYPE_INT64")] Int64 = 3, + [pbr::OriginalName("TYPE_UINT64")] Uint64 = 4, /// /// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if /// negative values are likely. /// - TYPE_INT32 = 5, - TYPE_FIXED64 = 6, - TYPE_FIXED32 = 7, - TYPE_BOOL = 8, - TYPE_STRING = 9, + [pbr::OriginalName("TYPE_INT32")] Int32 = 5, + [pbr::OriginalName("TYPE_FIXED64")] Fixed64 = 6, + [pbr::OriginalName("TYPE_FIXED32")] Fixed32 = 7, + [pbr::OriginalName("TYPE_BOOL")] Bool = 8, + [pbr::OriginalName("TYPE_STRING")] String = 9, /// /// Tag-delimited aggregate. /// - TYPE_GROUP = 10, + [pbr::OriginalName("TYPE_GROUP")] Group = 10, /// /// Length-delimited aggregate. /// - TYPE_MESSAGE = 11, + [pbr::OriginalName("TYPE_MESSAGE")] Message = 11, /// /// New in version 2. /// - TYPE_BYTES = 12, - TYPE_UINT32 = 13, - TYPE_ENUM = 14, - TYPE_SFIXED32 = 15, - TYPE_SFIXED64 = 16, + [pbr::OriginalName("TYPE_BYTES")] Bytes = 12, + [pbr::OriginalName("TYPE_UINT32")] Uint32 = 13, + [pbr::OriginalName("TYPE_ENUM")] Enum = 14, + [pbr::OriginalName("TYPE_SFIXED32")] Sfixed32 = 15, + [pbr::OriginalName("TYPE_SFIXED64")] Sfixed64 = 16, /// /// Uses ZigZag encoding. /// - TYPE_SINT32 = 17, + [pbr::OriginalName("TYPE_SINT32")] Sint32 = 17, /// /// Uses ZigZag encoding. /// - TYPE_SINT64 = 18, + [pbr::OriginalName("TYPE_SINT64")] Sint64 = 18, } internal enum Label { /// /// 0 is reserved for errors /// - LABEL_OPTIONAL = 1, - LABEL_REQUIRED = 2, + [pbr::OriginalName("LABEL_OPTIONAL")] Optional = 1, + [pbr::OriginalName("LABEL_REQUIRED")] Required = 2, /// /// TODO(sanjay): Should we add LABEL_MAP? /// - LABEL_REPEATED = 3, + [pbr::OriginalName("LABEL_REPEATED")] Repeated = 3, } } @@ -2563,7 +2562,6 @@ namespace Google.Protobuf.Reflection { ccEnableArenas_ = other.ccEnableArenas_; objcClassPrefix_ = other.objcClassPrefix_; csharpNamespace_ = other.csharpNamespace_; - javananoUseDeprecatedPackage_ = other.javananoUseDeprecatedPackage_; uninterpretedOption_ = other.uninterpretedOption_.Clone(); } @@ -2666,7 +2664,7 @@ namespace Google.Protobuf.Reflection { /// Field number for the "optimize_for" field. public const int OptimizeForFieldNumber = 9; - private global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode optimizeFor_ = global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode.SPEED; + private global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode optimizeFor_ = 0; public global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode OptimizeFor { get { return optimizeFor_; } set { @@ -2790,21 +2788,6 @@ namespace Google.Protobuf.Reflection { } } - /// Field number for the "javanano_use_deprecated_package" field. - public const int JavananoUseDeprecatedPackageFieldNumber = 38; - private bool javananoUseDeprecatedPackage_; - /// - /// Whether the nano proto compiler should generate in the deprecated non-nano - /// suffixed package. - /// - [global::System.ObsoleteAttribute()] - public bool JavananoUseDeprecatedPackage { - get { return javananoUseDeprecatedPackage_; } - set { - javananoUseDeprecatedPackage_ = value; - } - } - /// Field number for the "uninterpreted_option" field. public const int UninterpretedOptionFieldNumber = 999; private static readonly pb::FieldCodec _repeated_uninterpretedOption_codec @@ -2842,7 +2825,6 @@ namespace Google.Protobuf.Reflection { if (CcEnableArenas != other.CcEnableArenas) return false; if (ObjcClassPrefix != other.ObjcClassPrefix) return false; if (CsharpNamespace != other.CsharpNamespace) return false; - if (JavananoUseDeprecatedPackage != other.JavananoUseDeprecatedPackage) return false; if(!uninterpretedOption_.Equals(other.uninterpretedOption_)) return false; return true; } @@ -2854,7 +2836,7 @@ namespace Google.Protobuf.Reflection { if (JavaMultipleFiles != false) hash ^= JavaMultipleFiles.GetHashCode(); if (JavaGenerateEqualsAndHash != false) hash ^= JavaGenerateEqualsAndHash.GetHashCode(); if (JavaStringCheckUtf8 != false) hash ^= JavaStringCheckUtf8.GetHashCode(); - if (OptimizeFor != global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode.SPEED) hash ^= OptimizeFor.GetHashCode(); + if (OptimizeFor != 0) hash ^= OptimizeFor.GetHashCode(); if (GoPackage.Length != 0) hash ^= GoPackage.GetHashCode(); if (CcGenericServices != false) hash ^= CcGenericServices.GetHashCode(); if (JavaGenericServices != false) hash ^= JavaGenericServices.GetHashCode(); @@ -2863,7 +2845,6 @@ namespace Google.Protobuf.Reflection { if (CcEnableArenas != false) hash ^= CcEnableArenas.GetHashCode(); if (ObjcClassPrefix.Length != 0) hash ^= ObjcClassPrefix.GetHashCode(); if (CsharpNamespace.Length != 0) hash ^= CsharpNamespace.GetHashCode(); - if (JavananoUseDeprecatedPackage != false) hash ^= JavananoUseDeprecatedPackage.GetHashCode(); hash ^= uninterpretedOption_.GetHashCode(); return hash; } @@ -2881,7 +2862,7 @@ namespace Google.Protobuf.Reflection { output.WriteRawTag(66); output.WriteString(JavaOuterClassname); } - if (OptimizeFor != global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode.SPEED) { + if (OptimizeFor != 0) { output.WriteRawTag(72); output.WriteEnum((int) OptimizeFor); } @@ -2929,10 +2910,6 @@ namespace Google.Protobuf.Reflection { output.WriteRawTag(170, 2); output.WriteString(CsharpNamespace); } - if (JavananoUseDeprecatedPackage != false) { - output.WriteRawTag(176, 2); - output.WriteBool(JavananoUseDeprecatedPackage); - } uninterpretedOption_.WriteTo(output, _repeated_uninterpretedOption_codec); } @@ -2953,7 +2930,7 @@ namespace Google.Protobuf.Reflection { if (JavaStringCheckUtf8 != false) { size += 2 + 1; } - if (OptimizeFor != global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode.SPEED) { + if (OptimizeFor != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) OptimizeFor); } if (GoPackage.Length != 0) { @@ -2980,9 +2957,6 @@ namespace Google.Protobuf.Reflection { if (CsharpNamespace.Length != 0) { size += 2 + pb::CodedOutputStream.ComputeStringSize(CsharpNamespace); } - if (JavananoUseDeprecatedPackage != false) { - size += 2 + 1; - } size += uninterpretedOption_.CalculateSize(_repeated_uninterpretedOption_codec); return size; } @@ -3006,7 +2980,7 @@ namespace Google.Protobuf.Reflection { if (other.JavaStringCheckUtf8 != false) { JavaStringCheckUtf8 = other.JavaStringCheckUtf8; } - if (other.OptimizeFor != global::Google.Protobuf.Reflection.FileOptions.Types.OptimizeMode.SPEED) { + if (other.OptimizeFor != 0) { OptimizeFor = other.OptimizeFor; } if (other.GoPackage.Length != 0) { @@ -3033,9 +3007,6 @@ namespace Google.Protobuf.Reflection { if (other.CsharpNamespace.Length != 0) { CsharpNamespace = other.CsharpNamespace; } - if (other.JavananoUseDeprecatedPackage != false) { - JavananoUseDeprecatedPackage = other.JavananoUseDeprecatedPackage; - } uninterpretedOption_.Add(other.uninterpretedOption_); } @@ -3102,10 +3073,6 @@ namespace Google.Protobuf.Reflection { CsharpNamespace = input.ReadString(); break; } - case 304: { - JavananoUseDeprecatedPackage = input.ReadBool(); - break; - } case 7994: { uninterpretedOption_.AddEntriesFrom(input, _repeated_uninterpretedOption_codec); break; @@ -3125,15 +3092,15 @@ namespace Google.Protobuf.Reflection { /// /// Generate complete code for parsing, serialization, /// - SPEED = 1, + [pbr::OriginalName("SPEED")] Speed = 1, /// /// etc. /// - CODE_SIZE = 2, + [pbr::OriginalName("CODE_SIZE")] CodeSize = 2, /// /// Generate code using MessageLite and the lite runtime. /// - LITE_RUNTIME = 3, + [pbr::OriginalName("LITE_RUNTIME")] LiteRuntime = 3, } } @@ -3436,7 +3403,7 @@ namespace Google.Protobuf.Reflection { /// Field number for the "ctype" field. public const int CtypeFieldNumber = 1; - private global::Google.Protobuf.Reflection.FieldOptions.Types.CType ctype_ = global::Google.Protobuf.Reflection.FieldOptions.Types.CType.STRING; + private global::Google.Protobuf.Reflection.FieldOptions.Types.CType ctype_ = 0; /// /// The ctype option instructs the C++ code generator to use a different /// representation of the field than it normally would. See the specific @@ -3469,7 +3436,7 @@ namespace Google.Protobuf.Reflection { /// Field number for the "jstype" field. public const int JstypeFieldNumber = 6; - private global::Google.Protobuf.Reflection.FieldOptions.Types.JSType jstype_ = global::Google.Protobuf.Reflection.FieldOptions.Types.JSType.JS_NORMAL; + private global::Google.Protobuf.Reflection.FieldOptions.Types.JSType jstype_ = 0; /// /// The jstype option determines the JavaScript type used for values of the /// field. The option is permitted only for 64 bit integral and fixed types @@ -3591,9 +3558,9 @@ namespace Google.Protobuf.Reflection { public override int GetHashCode() { int hash = 1; - if (Ctype != global::Google.Protobuf.Reflection.FieldOptions.Types.CType.STRING) hash ^= Ctype.GetHashCode(); + if (Ctype != 0) hash ^= Ctype.GetHashCode(); if (Packed != false) hash ^= Packed.GetHashCode(); - if (Jstype != global::Google.Protobuf.Reflection.FieldOptions.Types.JSType.JS_NORMAL) hash ^= Jstype.GetHashCode(); + if (Jstype != 0) hash ^= Jstype.GetHashCode(); if (Lazy != false) hash ^= Lazy.GetHashCode(); if (Deprecated != false) hash ^= Deprecated.GetHashCode(); if (Weak != false) hash ^= Weak.GetHashCode(); @@ -3606,7 +3573,7 @@ namespace Google.Protobuf.Reflection { } public void WriteTo(pb::CodedOutputStream output) { - if (Ctype != global::Google.Protobuf.Reflection.FieldOptions.Types.CType.STRING) { + if (Ctype != 0) { output.WriteRawTag(8); output.WriteEnum((int) Ctype); } @@ -3622,7 +3589,7 @@ namespace Google.Protobuf.Reflection { output.WriteRawTag(40); output.WriteBool(Lazy); } - if (Jstype != global::Google.Protobuf.Reflection.FieldOptions.Types.JSType.JS_NORMAL) { + if (Jstype != 0) { output.WriteRawTag(48); output.WriteEnum((int) Jstype); } @@ -3635,13 +3602,13 @@ namespace Google.Protobuf.Reflection { public int CalculateSize() { int size = 0; - if (Ctype != global::Google.Protobuf.Reflection.FieldOptions.Types.CType.STRING) { + if (Ctype != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Ctype); } if (Packed != false) { size += 1 + 1; } - if (Jstype != global::Google.Protobuf.Reflection.FieldOptions.Types.JSType.JS_NORMAL) { + if (Jstype != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Jstype); } if (Lazy != false) { @@ -3661,13 +3628,13 @@ namespace Google.Protobuf.Reflection { if (other == null) { return; } - if (other.Ctype != global::Google.Protobuf.Reflection.FieldOptions.Types.CType.STRING) { + if (other.Ctype != 0) { Ctype = other.Ctype; } if (other.Packed != false) { Packed = other.Packed; } - if (other.Jstype != global::Google.Protobuf.Reflection.FieldOptions.Types.JSType.JS_NORMAL) { + if (other.Jstype != 0) { Jstype = other.Jstype; } if (other.Lazy != false) { @@ -3729,24 +3696,24 @@ namespace Google.Protobuf.Reflection { /// /// Default mode. /// - STRING = 0, - CORD = 1, - STRING_PIECE = 2, + [pbr::OriginalName("STRING")] String = 0, + [pbr::OriginalName("CORD")] Cord = 1, + [pbr::OriginalName("STRING_PIECE")] StringPiece = 2, } internal enum JSType { /// /// Use the default type. /// - JS_NORMAL = 0, + [pbr::OriginalName("JS_NORMAL")] JsNormal = 0, /// /// Use JavaScript strings. /// - JS_STRING = 1, + [pbr::OriginalName("JS_STRING")] JsString = 1, /// /// Use JavaScript numbers. /// - JS_NUMBER = 2, + [pbr::OriginalName("JS_NUMBER")] JsNumber = 2, } } diff --git a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs index c6caaec64..6c6f6ee05 100644 --- a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs @@ -30,9 +30,8 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion -using System; -using System.Linq; using Google.Protobuf.Compatibility; +using System; namespace Google.Protobuf.Reflection { @@ -41,20 +40,35 @@ namespace Google.Protobuf.Reflection /// public sealed class FieldDescriptor : DescriptorBase, IComparable { - private readonly FieldDescriptorProto proto; private EnumDescriptor enumType; private MessageDescriptor messageType; - private readonly MessageDescriptor containingType; - private readonly OneofDescriptor containingOneof; private FieldType fieldType; private readonly string propertyName; // Annoyingly, needed in Crosslink. private IFieldAccessor accessor; + /// + /// Get the field's containing message type. + /// + public MessageDescriptor ContainingType { get; } + + /// + /// Returns the oneof containing this field, or null if it is not part of a oneof. + /// + public OneofDescriptor ContainingOneof { get; } + + /// + /// The effective JSON name for this field. This is usually the lower-camel-cased form of the field name, + /// but can be overridden using the json_name option in the .proto file. + /// + public string JsonName { get; } + + internal FieldDescriptorProto Proto { get; } + internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, string propertyName) : base(file, file.ComputeFullName(parent, proto.Name), index) { - this.proto = proto; + Proto = proto; if (proto.Type != 0) { fieldType = GetFieldTypeFromProtoType(proto.Type); @@ -64,7 +78,7 @@ namespace Google.Protobuf.Reflection { throw new DescriptorValidationException(this, "Field numbers must be positive integers."); } - containingType = parent; + ContainingType = parent; // OneofIndex "defaults" to -1 due to a hack in FieldDescriptor.OnConstruction. if (proto.OneofIndex != -1) { @@ -73,7 +87,7 @@ namespace Google.Protobuf.Reflection throw new DescriptorValidationException(this, $"FieldDescriptorProto.oneof_index is out of range for type {parent.Name}"); } - containingOneof = parent.Oneofs[proto.OneofIndex]; + ContainingOneof = parent.Oneofs[proto.OneofIndex]; } file.DescriptorPool.AddSymbol(this); @@ -83,14 +97,14 @@ namespace Google.Protobuf.Reflection // We could trust the generated code and check whether the type of the property is // a MapField, but that feels a tad nasty. this.propertyName = propertyName; + JsonName = Proto.JsonName == "" ? JsonFormatter.ToCamelCase(Proto.Name) : Proto.JsonName; } + /// /// The brief name of the descriptor's target. /// - public override string Name { get { return proto.Name; } } - - internal FieldDescriptorProto Proto { get { return proto; } } + public override string Name => Proto.Name; /// /// Returns the accessor for this field. @@ -110,7 +124,7 @@ namespace Google.Protobuf.Reflection /// and this property will return null. /// /// - public IFieldAccessor Accessor { get { return accessor; } } + public IFieldAccessor Accessor => accessor; /// /// Maps a field type as included in the .proto file to a FieldType. @@ -119,41 +133,41 @@ namespace Google.Protobuf.Reflection { switch (type) { - case FieldDescriptorProto.Types.Type.TYPE_DOUBLE: + case FieldDescriptorProto.Types.Type.Double: return FieldType.Double; - case FieldDescriptorProto.Types.Type.TYPE_FLOAT: + case FieldDescriptorProto.Types.Type.Float: return FieldType.Float; - case FieldDescriptorProto.Types.Type.TYPE_INT64: + case FieldDescriptorProto.Types.Type.Int64: return FieldType.Int64; - case FieldDescriptorProto.Types.Type.TYPE_UINT64: + case FieldDescriptorProto.Types.Type.Uint64: return FieldType.UInt64; - case FieldDescriptorProto.Types.Type.TYPE_INT32: + case FieldDescriptorProto.Types.Type.Int32: return FieldType.Int32; - case FieldDescriptorProto.Types.Type.TYPE_FIXED64: + case FieldDescriptorProto.Types.Type.Fixed64: return FieldType.Fixed64; - case FieldDescriptorProto.Types.Type.TYPE_FIXED32: + case FieldDescriptorProto.Types.Type.Fixed32: return FieldType.Fixed32; - case FieldDescriptorProto.Types.Type.TYPE_BOOL: + case FieldDescriptorProto.Types.Type.Bool: return FieldType.Bool; - case FieldDescriptorProto.Types.Type.TYPE_STRING: + case FieldDescriptorProto.Types.Type.String: return FieldType.String; - case FieldDescriptorProto.Types.Type.TYPE_GROUP: + case FieldDescriptorProto.Types.Type.Group: return FieldType.Group; - case FieldDescriptorProto.Types.Type.TYPE_MESSAGE: + case FieldDescriptorProto.Types.Type.Message: return FieldType.Message; - case FieldDescriptorProto.Types.Type.TYPE_BYTES: + case FieldDescriptorProto.Types.Type.Bytes: return FieldType.Bytes; - case FieldDescriptorProto.Types.Type.TYPE_UINT32: + case FieldDescriptorProto.Types.Type.Uint32: return FieldType.UInt32; - case FieldDescriptorProto.Types.Type.TYPE_ENUM: + case FieldDescriptorProto.Types.Type.Enum: return FieldType.Enum; - case FieldDescriptorProto.Types.Type.TYPE_SFIXED32: + case FieldDescriptorProto.Types.Type.Sfixed32: return FieldType.SFixed32; - case FieldDescriptorProto.Types.Type.TYPE_SFIXED64: + case FieldDescriptorProto.Types.Type.Sfixed64: return FieldType.SFixed64; - case FieldDescriptorProto.Types.Type.TYPE_SINT32: + case FieldDescriptorProto.Types.Type.Sint32: return FieldType.SInt32; - case FieldDescriptorProto.Types.Type.TYPE_SINT64: + case FieldDescriptorProto.Types.Type.Sint64: return FieldType.SInt64; default: throw new ArgumentException("Invalid type specified"); @@ -163,62 +177,32 @@ namespace Google.Protobuf.Reflection /// /// Returns true if this field is a repeated field; false otherwise. /// - public bool IsRepeated - { - get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REPEATED; } - } + public bool IsRepeated => Proto.Label == FieldDescriptorProto.Types.Label.Repeated; /// /// Returns true if this field is a map field; false otherwise. /// - public bool IsMap - { - get { return fieldType == FieldType.Message && messageType.Proto.Options != null && messageType.Proto.Options.MapEntry; } - } + public bool IsMap => fieldType == FieldType.Message && messageType.Proto.Options != null && messageType.Proto.Options.MapEntry; /// /// Returns true if this field is a packed, repeated field; false otherwise. /// - public bool IsPacked - { + public bool IsPacked => // Note the || rather than && here - we're effectively defaulting to packed, because that *is* // the default in proto3, which is all we support. We may give the wrong result for the protos // within descriptor.proto, but that's okay, as they're never exposed and we don't use IsPacked // within the runtime. - get { return Proto.Options == null || Proto.Options.Packed; } - } - - /// - /// Get the field's containing message type. - /// - public MessageDescriptor ContainingType - { - get { return containingType; } - } - - /// - /// Returns the oneof containing this field, or null if it is not part of a oneof. - /// - public OneofDescriptor ContainingOneof - { - get { return containingOneof; } - } - + Proto.Options == null || Proto.Options.Packed; + /// /// Returns the type of the field. /// - public FieldType FieldType - { - get { return fieldType; } - } + public FieldType FieldType => fieldType; /// /// Returns the field number declared in the proto file. /// - public int FieldNumber - { - get { return Proto.Number; } - } + public int FieldNumber => Proto.Number; /// /// Compares this descriptor with another one, ordering in "canonical" order @@ -228,7 +212,7 @@ namespace Google.Protobuf.Reflection /// public int CompareTo(FieldDescriptor other) { - if (other.containingType != containingType) + if (other.ContainingType != ContainingType) { throw new ArgumentException("FieldDescriptors can only be compared to other FieldDescriptors " + "for fields of the same message type."); @@ -331,14 +315,14 @@ namespace Google.Protobuf.Reflection File.DescriptorPool.AddFieldByNumber(this); - if (containingType != null && containingType.Proto.Options != null && containingType.Proto.Options.MessageSetWireFormat) + if (ContainingType != null && ContainingType.Proto.Options != null && ContainingType.Proto.Options.MessageSetWireFormat) { throw new DescriptorValidationException(this, "MessageSet format is not supported."); } - accessor = CreateAccessor(propertyName); + accessor = CreateAccessor(); } - private IFieldAccessor CreateAccessor(string propertyName) + private IFieldAccessor CreateAccessor() { // If we're given no property name, that's because we really don't want an accessor. // (At the moment, that means it's a map entry message...) @@ -346,10 +330,10 @@ namespace Google.Protobuf.Reflection { return null; } - var property = containingType.ClrType.GetProperty(propertyName); + var property = ContainingType.ClrType.GetProperty(propertyName); if (property == null) { - throw new DescriptorValidationException(this, $"Property {propertyName} not found in {containingType.ClrType}"); + throw new DescriptorValidationException(this, $"Property {propertyName} not found in {ContainingType.ClrType}"); } return IsMap ? new MapFieldAccessor(property, this) : IsRepeated ? new RepeatedFieldAccessor(property, this) diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs index f5798d1ea..f5a835e5c 100644 --- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs @@ -102,8 +102,8 @@ namespace Google.Protobuf.Reflection var map = new Dictionary(); foreach (var field in fields) { - map[JsonFormatter.ToCamelCase(field.Name)] = field; map[field.Name] = field; + map[field.JsonName] = field; } return new ReadOnlyDictionary(map); } diff --git a/csharp/src/Google.Protobuf/Reflection/OriginalNameAttribute.cs b/csharp/src/Google.Protobuf/Reflection/OriginalNameAttribute.cs new file mode 100644 index 000000000..27f9ab98c --- /dev/null +++ b/csharp/src/Google.Protobuf/Reflection/OriginalNameAttribute.cs @@ -0,0 +1,58 @@ +#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.Reflection +{ + /// + /// Specifies the original name (in the .proto file) of a named element, + /// such as an enum value. + /// + [AttributeUsage(AttributeTargets.Field)] + public class OriginalNameAttribute : Attribute + { + /// + /// The name of the element in the .proto file. + /// + public string Name { get; set; } + + /// + /// Constructs a new attribute instance for the given name. + /// + /// The name of the element in the .proto file. + public OriginalNameAttribute(string name) + { + Name = ProtoPreconditions.CheckNotNull(name, nameof(name)); + } + } +} \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs index 48cd99a13..871a383f0 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/Any.cs @@ -24,9 +24,10 @@ namespace Google.Protobuf.WellKnownTypes { byte[] descriptorData = global::System.Convert.FromBase64String( string.Concat( "Chlnb29nbGUvcHJvdG9idWYvYW55LnByb3RvEg9nb29nbGUucHJvdG9idWYi", - "JgoDQW55EhAKCHR5cGVfdXJsGAEgASgJEg0KBXZhbHVlGAIgASgMQksKE2Nv", - "bS5nb29nbGUucHJvdG9idWZCCEFueVByb3RvUAGgAQGiAgNHUEKqAh5Hb29n", - "bGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNiBnByb3RvMw==")); + "JgoDQW55EhAKCHR5cGVfdXJsGAEgASgJEg0KBXZhbHVlGAIgASgMQnIKE2Nv", + "bS5nb29nbGUucHJvdG9idWZCCEFueVByb3RvUAFaJWdpdGh1Yi5jb20vZ29s", + "YW5nL3Byb3RvYnVmL3B0eXBlcy9hbnmgAQGiAgNHUEKqAh5Hb29nbGUuUHJv", + "dG9idWYuV2VsbEtub3duVHlwZXNiBnByb3RvMw==")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { @@ -38,8 +39,36 @@ namespace Google.Protobuf.WellKnownTypes { } #region Messages /// - /// `Any` contains an arbitrary serialized message along with a URL - /// that describes the type of the serialized message. + /// `Any` contains an arbitrary serialized protocol buffer message along with a + /// URL that describes the type of the serialized message. + /// + /// Protobuf library provides support to pack/unpack Any values in the form + /// of utility functions or additional generated methods of the Any type. + /// + /// Example 1: Pack and unpack a message in C++. + /// + /// Foo foo = ...; + /// Any any; + /// any.PackFrom(foo); + /// ... + /// if (any.UnpackTo(&foo)) { + /// ... + /// } + /// + /// Example 2: Pack and unpack a message in Java. + /// + /// Foo foo = ...; + /// Any any = Any.pack(foo); + /// ... + /// if (any.is(Foo.class)) { + /// foo = any.unpack(Foo.class); + /// } + /// + /// The pack methods provided by protobuf library will by default use + /// 'type.googleapis.com/full.type.name' as the type URL and the unpack + /// methods only use the fully qualified type name after the last '/' + /// in the type URL, for example "foo.bar.com/x/y.z" will yield type + /// name "y.z". /// /// JSON /// ==== @@ -102,7 +131,7 @@ namespace Google.Protobuf.WellKnownTypes { private string typeUrl_ = ""; /// /// A URL/resource name whose content describes the type of the - /// serialized message. + /// serialized protocol buffer message. /// /// For URLs which use the schema `http`, `https`, or no schema, the /// following restrictions and interpretations apply: @@ -110,6 +139,8 @@ namespace Google.Protobuf.WellKnownTypes { /// * If no schema is provided, `https` is assumed. /// * The last segment of the URL's path must represent the fully /// qualified name of the type (as in `path/google.protobuf.Duration`). + /// The name should be in a canonical form (e.g., leading "." is + /// not accepted). /// * An HTTP GET on the URL must yield a [google.protobuf.Type][] /// value in binary format, or produce an error. /// * Applications are allowed to cache lookup results based on the @@ -132,7 +163,7 @@ namespace Google.Protobuf.WellKnownTypes { public const int ValueFieldNumber = 2; private pb::ByteString value_ = pb::ByteString.Empty; /// - /// Must be valid serialized data of the above specified type. + /// Must be a valid serialized protocol buffer of the above specified type. /// public pb::ByteString Value { get { return value_; } diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs index 9d43856ee..f4fac7386 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/AnyPartial.cs @@ -36,11 +36,27 @@ namespace Google.Protobuf.WellKnownTypes { public partial class Any { + private const string DefaultPrefix = "type.googleapis.com"; + // This could be moved to MessageDescriptor if we wanted to, but keeping it here means // all the Any-specific code is in the same place. - private static string GetTypeUrl(MessageDescriptor descriptor) + private static string GetTypeUrl(MessageDescriptor descriptor, string prefix) => + prefix.EndsWith("/") ? prefix + descriptor.FullName : prefix + "/" + descriptor.FullName; + + /// + /// Retrieves the type name for a type URL. This is always just the last part of the URL, + /// after the trailing slash. No validation of anything before the trailing slash is performed. + /// If the type URL does not include a slash, an empty string is returned rather than an exception + /// being thrown; this won't match any types, and the calling code is probably in a better position + /// to give a meaningful error. + /// There is no handling of fragments or queries at the moment. + /// + /// The URL to extract the type name from + /// The type name + internal static string GetTypeName(string typeUrl) { - return "type.googleapis.com/" + descriptor.FullName; + int lastSlash = typeUrl.LastIndexOf('/'); + return lastSlash == -1 ? "" : typeUrl.Substring(lastSlash + 1); } /// @@ -55,25 +71,37 @@ namespace Google.Protobuf.WellKnownTypes // Note: this doesn't perform as well is it might. We could take a MessageParser in an alternative overload, // which would be expected to perform slightly better... although the difference is likely to be negligible. T target = new T(); - string targetTypeUrl = GetTypeUrl(target.Descriptor); - if (TypeUrl != targetTypeUrl) + if (GetTypeName(TypeUrl) != target.Descriptor.FullName) { - throw new InvalidProtocolBufferException(string.Format("Type url for {0} is {1}; Any message's type url is {2}", - target.Descriptor.Name, targetTypeUrl, TypeUrl)); + throw new InvalidProtocolBufferException( + $"Full type name for {target.Descriptor.Name} is {target.Descriptor.FullName}; Any message's type url is {TypeUrl}"); } target.MergeFrom(Value); return target; } /// - /// Packs the specified message into an Any message. + /// Packs the specified message into an Any message using a type URL prefix of "type.googleapis.com". /// /// The message to pack. /// An Any message with the content and type URL of . - public static Any Pack(IMessage message) + public static Any Pack(IMessage message) => Pack(message, DefaultPrefix); + + /// + /// Packs the specified message into an Any message using the specified type URL prefix. + /// + /// The message to pack. + /// The prefix for the type URL. + /// An Any message with the content and type URL of . + public static Any Pack(IMessage message, string typeUrlPrefix) { - ProtoPreconditions.CheckNotNull(message, "message"); - return new Any { TypeUrl = GetTypeUrl(message.Descriptor), Value = message.ToByteString() }; + ProtoPreconditions.CheckNotNull(message, nameof(message)); + ProtoPreconditions.CheckNotNull(typeUrlPrefix, nameof(typeUrlPrefix)); + return new Any + { + TypeUrl = GetTypeUrl(message.Descriptor, typeUrlPrefix), + Value = message.ToByteString() + }; } } } diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs index de7aea3a0..e568a2c98 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/Api.cs @@ -185,7 +185,7 @@ namespace Google.Protobuf.WellKnownTypes { /// Field number for the "syntax" field. public const int SyntaxFieldNumber = 7; - private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2; + private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = 0; /// /// The source syntax of the service. /// @@ -225,7 +225,7 @@ namespace Google.Protobuf.WellKnownTypes { if (Version.Length != 0) hash ^= Version.GetHashCode(); if (sourceContext_ != null) hash ^= SourceContext.GetHashCode(); hash ^= mixins_.GetHashCode(); - if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) hash ^= Syntax.GetHashCode(); + if (Syntax != 0) hash ^= Syntax.GetHashCode(); return hash; } @@ -249,7 +249,7 @@ namespace Google.Protobuf.WellKnownTypes { output.WriteMessage(SourceContext); } mixins_.WriteTo(output, _repeated_mixins_codec); - if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) { + if (Syntax != 0) { output.WriteRawTag(56); output.WriteEnum((int) Syntax); } @@ -269,7 +269,7 @@ namespace Google.Protobuf.WellKnownTypes { size += 1 + pb::CodedOutputStream.ComputeMessageSize(SourceContext); } size += mixins_.CalculateSize(_repeated_mixins_codec); - if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) { + if (Syntax != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Syntax); } return size; @@ -294,7 +294,7 @@ namespace Google.Protobuf.WellKnownTypes { SourceContext.MergeFrom(other.SourceContext); } mixins_.Add(other.mixins_); - if (other.Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) { + if (other.Syntax != 0) { Syntax = other.Syntax; } } @@ -458,7 +458,7 @@ namespace Google.Protobuf.WellKnownTypes { /// Field number for the "syntax" field. public const int SyntaxFieldNumber = 7; - private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2; + private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = 0; /// /// The source syntax of this method. /// @@ -498,7 +498,7 @@ namespace Google.Protobuf.WellKnownTypes { if (ResponseTypeUrl.Length != 0) hash ^= ResponseTypeUrl.GetHashCode(); if (ResponseStreaming != false) hash ^= ResponseStreaming.GetHashCode(); hash ^= options_.GetHashCode(); - if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) hash ^= Syntax.GetHashCode(); + if (Syntax != 0) hash ^= Syntax.GetHashCode(); return hash; } @@ -528,7 +528,7 @@ namespace Google.Protobuf.WellKnownTypes { output.WriteBool(ResponseStreaming); } options_.WriteTo(output, _repeated_options_codec); - if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) { + if (Syntax != 0) { output.WriteRawTag(56); output.WriteEnum((int) Syntax); } @@ -552,7 +552,7 @@ namespace Google.Protobuf.WellKnownTypes { size += 1 + 1; } size += options_.CalculateSize(_repeated_options_codec); - if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) { + if (Syntax != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Syntax); } return size; @@ -578,7 +578,7 @@ namespace Google.Protobuf.WellKnownTypes { ResponseStreaming = other.ResponseStreaming; } options_.Add(other.options_); - if (other.Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) { + if (other.Syntax != 0) { Syntax = other.Syntax; } } diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs index 73e221d49..f17358f4b 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs @@ -25,9 +25,10 @@ namespace Google.Protobuf.WellKnownTypes { string.Concat( "Ch5nb29nbGUvcHJvdG9idWYvZHVyYXRpb24ucHJvdG8SD2dvb2dsZS5wcm90", "b2J1ZiIqCghEdXJhdGlvbhIPCgdzZWNvbmRzGAEgASgDEg0KBW5hbm9zGAIg", - "ASgFQlAKE2NvbS5nb29nbGUucHJvdG9idWZCDUR1cmF0aW9uUHJvdG9QAaAB", - "AaICA0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25vd25UeXBlc2IGcHJv", - "dG8z")); + "ASgFQnwKE2NvbS5nb29nbGUucHJvdG9idWZCDUR1cmF0aW9uUHJvdG9QAVoq", + "Z2l0aHViLmNvbS9nb2xhbmcvcHJvdG9idWYvcHR5cGVzL2R1cmF0aW9uoAEB", + "ogIDR1BCqgIeR29vZ2xlLlByb3RvYnVmLldlbGxLbm93blR5cGVzYgZwcm90", + "bzM=")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs index 6cad41241..e08ea2400 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs @@ -24,9 +24,10 @@ namespace Google.Protobuf.WellKnownTypes { byte[] descriptorData = global::System.Convert.FromBase64String( string.Concat( "Chtnb29nbGUvcHJvdG9idWYvZW1wdHkucHJvdG8SD2dvb2dsZS5wcm90b2J1", - "ZiIHCgVFbXB0eUJQChNjb20uZ29vZ2xlLnByb3RvYnVmQgpFbXB0eVByb3Rv", - "UAGgAQH4AQGiAgNHUEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlw", - "ZXNiBnByb3RvMw==")); + "ZiIHCgVFbXB0eUJ5ChNjb20uZ29vZ2xlLnByb3RvYnVmQgpFbXB0eVByb3Rv", + "UAFaJ2dpdGh1Yi5jb20vZ29sYW5nL3Byb3RvYnVmL3B0eXBlcy9lbXB0eaAB", + "AfgBAaICA0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25vd25UeXBlc2IG", + "cHJvdG8z")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs index 2ba2b96fc..6f0a64d6a 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs @@ -86,7 +86,7 @@ namespace Google.Protobuf.WellKnownTypes { /// operation applies to all fields (as if a FieldMask of all fields /// had been specified). /// - /// Note that a field mask does not necessarily applies to the + /// Note that a field mask does not necessarily apply to the /// top-level response message. In case of a REST get operation, the /// field mask applies directly to the response, but in case of a REST /// list operation, the mask instead applies to each individual message @@ -159,6 +159,33 @@ namespace Google.Protobuf.WellKnownTypes { /// { /// mask: "user.displayName,photo" /// } + /// + /// # Field Masks and Oneof Fields + /// + /// Field masks treat fields in oneofs just as regular fields. Consider the + /// following message: + /// + /// message SampleMessage { + /// oneof test_oneof { + /// string name = 4; + /// SubMessage sub_message = 9; + /// } + /// } + /// + /// The field mask can be: + /// + /// mask { + /// paths: "name" + /// } + /// + /// Or: + /// + /// mask { + /// paths: "sub_message" + /// } + /// + /// Note that oneof type names ("test_oneof" in this case) cannot be used in + /// paths. /// [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] public sealed partial class FieldMask : pb::IMessage { diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs index 8e2ce8cf7..edc8940db 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs @@ -34,9 +34,10 @@ namespace Google.Protobuf.WellKnownTypes { "ABIwCgpsaXN0X3ZhbHVlGAYgASgLMhouZ29vZ2xlLnByb3RvYnVmLkxpc3RW", "YWx1ZUgAQgYKBGtpbmQiMwoJTGlzdFZhbHVlEiYKBnZhbHVlcxgBIAMoCzIW", "Lmdvb2dsZS5wcm90b2J1Zi5WYWx1ZSobCglOdWxsVmFsdWUSDgoKTlVMTF9W", - "QUxVRRAAQk4KE2NvbS5nb29nbGUucHJvdG9idWZCC1N0cnVjdFByb3RvUAGg", - "AQGiAgNHUEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNiBnBy", - "b3RvMw==")); + "QUxVRRAAQoEBChNjb20uZ29vZ2xlLnByb3RvYnVmQgtTdHJ1Y3RQcm90b1AB", + "WjFnaXRodWIuY29tL2dvbGFuZy9wcm90b2J1Zi9wdHlwZXMvc3RydWN0O3N0", + "cnVjdHBioAEBogIDR1BCqgIeR29vZ2xlLlByb3RvYnVmLldlbGxLbm93blR5", + "cGVzYgZwcm90bzM=")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.WellKnownTypes.NullValue), }, new pbr::GeneratedClrTypeInfo[] { @@ -59,7 +60,7 @@ namespace Google.Protobuf.WellKnownTypes { /// /// Null value. /// - NULL_VALUE = 0, + [pbr::OriginalName("NULL_VALUE")] NullValue = 0, } #endregion @@ -108,7 +109,7 @@ namespace Google.Protobuf.WellKnownTypes { = new pbc::MapField.Codec(pb::FieldCodec.ForString(10), pb::FieldCodec.ForMessage(18, global::Google.Protobuf.WellKnownTypes.Value.Parser), 10); private readonly pbc::MapField fields_ = new pbc::MapField(); /// - /// Map of dynamically typed values. + /// Unordered map of dynamically typed values. /// public pbc::MapField Fields { get { return fields_; } @@ -234,7 +235,7 @@ namespace Google.Protobuf.WellKnownTypes { /// Represents a null value. /// public global::Google.Protobuf.WellKnownTypes.NullValue NullValue { - get { return kindCase_ == KindOneofCase.NullValue ? (global::Google.Protobuf.WellKnownTypes.NullValue) kind_ : global::Google.Protobuf.WellKnownTypes.NullValue.NULL_VALUE; } + get { return kindCase_ == KindOneofCase.NullValue ? (global::Google.Protobuf.WellKnownTypes.NullValue) kind_ : 0; } set { kind_ = value; kindCase_ = KindOneofCase.NullValue; diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs index 318b0f615..053b88bd3 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs @@ -25,9 +25,10 @@ namespace Google.Protobuf.WellKnownTypes { string.Concat( "Ch9nb29nbGUvcHJvdG9idWYvdGltZXN0YW1wLnByb3RvEg9nb29nbGUucHJv", "dG9idWYiKwoJVGltZXN0YW1wEg8KB3NlY29uZHMYASABKAMSDQoFbmFub3MY", - "AiABKAVCVAoTY29tLmdvb2dsZS5wcm90b2J1ZkIOVGltZXN0YW1wUHJvdG9Q", - "AaABAfgBAaICA0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25vd25UeXBl", - "c2IGcHJvdG8z")); + "AiABKAVCgQEKE2NvbS5nb29nbGUucHJvdG9idWZCDlRpbWVzdGFtcFByb3Rv", + "UAFaK2dpdGh1Yi5jb20vZ29sYW5nL3Byb3RvYnVmL3B0eXBlcy90aW1lc3Rh", + "bXCgAQH4AQGiAgNHUEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlw", + "ZXNiBnByb3RvMw==")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs index 8faa1d1c3..657c24645 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/Type.cs @@ -79,11 +79,11 @@ namespace Google.Protobuf.WellKnownTypes { /// /// Syntax `proto2`. /// - SYNTAX_PROTO2 = 0, + [pbr::OriginalName("SYNTAX_PROTO2")] Proto2 = 0, /// /// Syntax `proto3`. /// - SYNTAX_PROTO3 = 1, + [pbr::OriginalName("SYNTAX_PROTO3")] Proto3 = 1, } #endregion @@ -188,7 +188,7 @@ namespace Google.Protobuf.WellKnownTypes { /// Field number for the "syntax" field. public const int SyntaxFieldNumber = 6; - private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2; + private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = 0; /// /// The source syntax. /// @@ -226,7 +226,7 @@ namespace Google.Protobuf.WellKnownTypes { hash ^= oneofs_.GetHashCode(); hash ^= options_.GetHashCode(); if (sourceContext_ != null) hash ^= SourceContext.GetHashCode(); - if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) hash ^= Syntax.GetHashCode(); + if (Syntax != 0) hash ^= Syntax.GetHashCode(); return hash; } @@ -246,7 +246,7 @@ namespace Google.Protobuf.WellKnownTypes { output.WriteRawTag(42); output.WriteMessage(SourceContext); } - if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) { + if (Syntax != 0) { output.WriteRawTag(48); output.WriteEnum((int) Syntax); } @@ -263,7 +263,7 @@ namespace Google.Protobuf.WellKnownTypes { if (sourceContext_ != null) { size += 1 + pb::CodedOutputStream.ComputeMessageSize(SourceContext); } - if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) { + if (Syntax != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Syntax); } return size; @@ -285,7 +285,7 @@ namespace Google.Protobuf.WellKnownTypes { } SourceContext.MergeFrom(other.SourceContext); } - if (other.Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) { + if (other.Syntax != 0) { Syntax = other.Syntax; } } @@ -371,7 +371,7 @@ namespace Google.Protobuf.WellKnownTypes { /// Field number for the "kind" field. public const int KindFieldNumber = 1; - private global::Google.Protobuf.WellKnownTypes.Field.Types.Kind kind_ = global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TYPE_UNKNOWN; + private global::Google.Protobuf.WellKnownTypes.Field.Types.Kind kind_ = 0; /// /// The field type. /// @@ -384,7 +384,7 @@ namespace Google.Protobuf.WellKnownTypes { /// Field number for the "cardinality" field. public const int CardinalityFieldNumber = 2; - private global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality cardinality_ = global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality.CARDINALITY_UNKNOWN; + private global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality cardinality_ = 0; /// /// The field cardinality. /// @@ -526,8 +526,8 @@ namespace Google.Protobuf.WellKnownTypes { public override int GetHashCode() { int hash = 1; - if (Kind != global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TYPE_UNKNOWN) hash ^= Kind.GetHashCode(); - if (Cardinality != global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality.CARDINALITY_UNKNOWN) hash ^= Cardinality.GetHashCode(); + if (Kind != 0) hash ^= Kind.GetHashCode(); + if (Cardinality != 0) hash ^= Cardinality.GetHashCode(); if (Number != 0) hash ^= Number.GetHashCode(); if (Name.Length != 0) hash ^= Name.GetHashCode(); if (TypeUrl.Length != 0) hash ^= TypeUrl.GetHashCode(); @@ -544,11 +544,11 @@ namespace Google.Protobuf.WellKnownTypes { } public void WriteTo(pb::CodedOutputStream output) { - if (Kind != global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TYPE_UNKNOWN) { + if (Kind != 0) { output.WriteRawTag(8); output.WriteEnum((int) Kind); } - if (Cardinality != global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality.CARDINALITY_UNKNOWN) { + if (Cardinality != 0) { output.WriteRawTag(16); output.WriteEnum((int) Cardinality); } @@ -585,10 +585,10 @@ namespace Google.Protobuf.WellKnownTypes { public int CalculateSize() { int size = 0; - if (Kind != global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TYPE_UNKNOWN) { + if (Kind != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Kind); } - if (Cardinality != global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality.CARDINALITY_UNKNOWN) { + if (Cardinality != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Cardinality); } if (Number != 0) { @@ -620,10 +620,10 @@ namespace Google.Protobuf.WellKnownTypes { if (other == null) { return; } - if (other.Kind != global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TYPE_UNKNOWN) { + if (other.Kind != 0) { Kind = other.Kind; } - if (other.Cardinality != global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality.CARDINALITY_UNKNOWN) { + if (other.Cardinality != 0) { Cardinality = other.Cardinality; } if (other.Number != 0) { @@ -712,79 +712,79 @@ namespace Google.Protobuf.WellKnownTypes { /// /// Field type unknown. /// - TYPE_UNKNOWN = 0, + [pbr::OriginalName("TYPE_UNKNOWN")] TypeUnknown = 0, /// /// Field type double. /// - TYPE_DOUBLE = 1, + [pbr::OriginalName("TYPE_DOUBLE")] TypeDouble = 1, /// /// Field type float. /// - TYPE_FLOAT = 2, + [pbr::OriginalName("TYPE_FLOAT")] TypeFloat = 2, /// /// Field type int64. /// - TYPE_INT64 = 3, + [pbr::OriginalName("TYPE_INT64")] TypeInt64 = 3, /// /// Field type uint64. /// - TYPE_UINT64 = 4, + [pbr::OriginalName("TYPE_UINT64")] TypeUint64 = 4, /// /// Field type int32. /// - TYPE_INT32 = 5, + [pbr::OriginalName("TYPE_INT32")] TypeInt32 = 5, /// /// Field type fixed64. /// - TYPE_FIXED64 = 6, + [pbr::OriginalName("TYPE_FIXED64")] TypeFixed64 = 6, /// /// Field type fixed32. /// - TYPE_FIXED32 = 7, + [pbr::OriginalName("TYPE_FIXED32")] TypeFixed32 = 7, /// /// Field type bool. /// - TYPE_BOOL = 8, + [pbr::OriginalName("TYPE_BOOL")] TypeBool = 8, /// /// Field type string. /// - TYPE_STRING = 9, + [pbr::OriginalName("TYPE_STRING")] TypeString = 9, /// /// Field type group. Proto2 syntax only, and deprecated. /// - TYPE_GROUP = 10, + [pbr::OriginalName("TYPE_GROUP")] TypeGroup = 10, /// /// Field type message. /// - TYPE_MESSAGE = 11, + [pbr::OriginalName("TYPE_MESSAGE")] TypeMessage = 11, /// /// Field type bytes. /// - TYPE_BYTES = 12, + [pbr::OriginalName("TYPE_BYTES")] TypeBytes = 12, /// /// Field type uint32. /// - TYPE_UINT32 = 13, + [pbr::OriginalName("TYPE_UINT32")] TypeUint32 = 13, /// /// Field type enum. /// - TYPE_ENUM = 14, + [pbr::OriginalName("TYPE_ENUM")] TypeEnum = 14, /// /// Field type sfixed32. /// - TYPE_SFIXED32 = 15, + [pbr::OriginalName("TYPE_SFIXED32")] TypeSfixed32 = 15, /// /// Field type sfixed64. /// - TYPE_SFIXED64 = 16, + [pbr::OriginalName("TYPE_SFIXED64")] TypeSfixed64 = 16, /// /// Field type sint32. /// - TYPE_SINT32 = 17, + [pbr::OriginalName("TYPE_SINT32")] TypeSint32 = 17, /// /// Field type sint64. /// - TYPE_SINT64 = 18, + [pbr::OriginalName("TYPE_SINT64")] TypeSint64 = 18, } /// @@ -794,19 +794,19 @@ namespace Google.Protobuf.WellKnownTypes { /// /// For fields with unknown cardinality. /// - CARDINALITY_UNKNOWN = 0, + [pbr::OriginalName("CARDINALITY_UNKNOWN")] Unknown = 0, /// /// For optional fields. /// - CARDINALITY_OPTIONAL = 1, + [pbr::OriginalName("CARDINALITY_OPTIONAL")] Optional = 1, /// /// For required fields. Proto2 syntax only. /// - CARDINALITY_REQUIRED = 2, + [pbr::OriginalName("CARDINALITY_REQUIRED")] Required = 2, /// /// For repeated fields. /// - CARDINALITY_REPEATED = 3, + [pbr::OriginalName("CARDINALITY_REPEATED")] Repeated = 3, } } @@ -900,7 +900,7 @@ namespace Google.Protobuf.WellKnownTypes { /// Field number for the "syntax" field. public const int SyntaxFieldNumber = 5; - private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2; + private global::Google.Protobuf.WellKnownTypes.Syntax syntax_ = 0; /// /// The source syntax. /// @@ -936,7 +936,7 @@ namespace Google.Protobuf.WellKnownTypes { hash ^= enumvalue_.GetHashCode(); hash ^= options_.GetHashCode(); if (sourceContext_ != null) hash ^= SourceContext.GetHashCode(); - if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) hash ^= Syntax.GetHashCode(); + if (Syntax != 0) hash ^= Syntax.GetHashCode(); return hash; } @@ -955,7 +955,7 @@ namespace Google.Protobuf.WellKnownTypes { output.WriteRawTag(34); output.WriteMessage(SourceContext); } - if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) { + if (Syntax != 0) { output.WriteRawTag(40); output.WriteEnum((int) Syntax); } @@ -971,7 +971,7 @@ namespace Google.Protobuf.WellKnownTypes { if (sourceContext_ != null) { size += 1 + pb::CodedOutputStream.ComputeMessageSize(SourceContext); } - if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) { + if (Syntax != 0) { size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Syntax); } return size; @@ -992,7 +992,7 @@ namespace Google.Protobuf.WellKnownTypes { } SourceContext.MergeFrom(other.SourceContext); } - if (other.Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.SYNTAX_PROTO2) { + if (other.Syntax != 0) { Syntax = other.Syntax; } } diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs index d6796d1a7..c8867d0e2 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs @@ -29,10 +29,10 @@ namespace Google.Protobuf.WellKnownTypes { "KAMiHAoLVUludDY0VmFsdWUSDQoFdmFsdWUYASABKAQiGwoKSW50MzJWYWx1", "ZRINCgV2YWx1ZRgBIAEoBSIcCgtVSW50MzJWYWx1ZRINCgV2YWx1ZRgBIAEo", "DSIaCglCb29sVmFsdWUSDQoFdmFsdWUYASABKAgiHAoLU3RyaW5nVmFsdWUS", - "DQoFdmFsdWUYASABKAkiGwoKQnl0ZXNWYWx1ZRINCgV2YWx1ZRgBIAEoDEJT", - "ChNjb20uZ29vZ2xlLnByb3RvYnVmQg1XcmFwcGVyc1Byb3RvUAGgAQH4AQGi", - "AgNHUEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNiBnByb3Rv", - "Mw==")); + "DQoFdmFsdWUYASABKAkiGwoKQnl0ZXNWYWx1ZRINCgV2YWx1ZRgBIAEoDEJ/", + "ChNjb20uZ29vZ2xlLnByb3RvYnVmQg1XcmFwcGVyc1Byb3RvUAFaKmdpdGh1", + "Yi5jb20vZ29sYW5nL3Byb3RvYnVmL3B0eXBlcy93cmFwcGVyc6ABAfgBAaIC", + "A0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25vd25UeXBlc2IGcHJvdG8z")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { diff --git a/docs/swift/DesignDoc.md b/docs/swift/DesignDoc.md new file mode 100644 index 000000000..364a4d3f1 --- /dev/null +++ b/docs/swift/DesignDoc.md @@ -0,0 +1,674 @@ +# Protocol Buffers in Swift + +## Objective + +This document describes the user-facing API and internal implementation of +proto2 and proto3 messages in Apple’s Swift programming language. + +One of the key goals of protobufs is to provide idiomatic APIs for each +language. In that vein, **interoperability with Objective-C is a non-goal of +this proposal.** Protobuf users who need to pass messages between Objective-C +and Swift code in the same application should use the existing Objective-C proto +library. The goal of the effort described here is to provide an API for protobuf +messages that uses features specific to Swift—optional types, algebraic +enumerated types, value types, and so forth—in a natural way that will delight, +rather than surprise, users of the language. + +## Naming + +* By convention, both typical protobuf message names and Swift structs/classes + are `UpperCamelCase`, so for most messages, the name of a message can be the + same as the name of its generated type. (However, see the discussion below + about prefixes under [Packages](#packages).) + +* Enum cases in protobufs typically are `UPPERCASE_WITH_UNDERSCORES`, whereas + in Swift they are `lowerCamelCase` (as of the Swift 3 API design + guidelines). We will transform the names to match Swift convention, using + a whitelist similar to the Objective-C compiler plugin to handle commonly + used acronyms. + +* Typical fields in proto messages are `lowercase_with_underscores`, while in + Swift they are `lowerCamelCase`. We will transform the names to match + Swift convention by removing the underscores and uppercasing the subsequent + letter. + +## Swift reserved words + +Swift has a large set of reserved words—some always reserved and some +contextually reserved (that is, they can be used as identifiers in contexts +where they would not be confused). As of Swift 2.2, the set of always-reserved +words is: + +``` +_, #available, #column, #else, #elseif, #endif, #file, #function, #if, #line, +#selector, as, associatedtype, break, case, catch, class, continue, default, +defer, deinit, do, dynamicType, else, enum, extension, fallthrough, false, for, +func, guard, if, import, in, init, inout, internal, is, let, nil, operator, +private, protocol, public, repeat, rethrows, return, self, Self, static, +struct, subscript, super, switch, throw, throws, true, try, typealias, var, +where, while +``` + +The set of contextually reserved words is: + +``` +associativity, convenience, dynamic, didSet, final, get, infix, indirect, +lazy, left, mutating, none, nonmutating, optional, override, postfix, +precedence, prefix, Protocol, required, right, set, Type, unowned, weak, +willSet +``` + +It is possible to use any reserved word as an identifier by escaping it with +backticks (for example, ``let `class` = 5``). Other name-mangling schemes would +require us to transform the names themselves (for example, by appending an +underscore), which requires us to then ensure that the new name does not collide +with something else in the same namespace. + +While the backtick feature may not be widely known by all Swift developers, a +small amount of user education can address this and it seems like the best +approach. We can unconditionally surround all property names with backticks to +simplify generation. + +Some remapping will still be required, though, to avoid collisions between +generated properties and the names of methods and properties defined in the base +protocol/implementation of messages. + +# Features of Protocol Buffers + +This section describes how the features of the protocol buffer syntaxes (proto2 +and proto3) map to features in Swift—what the code generated from a proto will +look like, and how it will be implemented in the underlying library. + +## Packages + +Modules are the main form of namespacing in Swift, but they are not declared +using syntactic constructs like namespaces in C++ or packages in Java. Instead, +they are tied to build targets in Xcode (or, in the future with open-source +Swift, declarations in a Swift Package Manager manifest). They also do not +easily support nesting submodules (Clang module maps support this, but pure +Swift does not yet provide a way to define submodules). + +We will generate types with fully-qualified underscore-delimited names. For +example, a message `Baz` in package `foo.bar` would generate a struct named +`Foo_Bar_Baz`. For each fully-qualified proto message, there will be exactly one +unique type symbol emitted in the generated binary. + +Users are likely to balk at the ugliness of underscore-delimited names for every +generated type. To improve upon this situation, we will add a new string file +level option, `swift_package_typealias`, that can be added to `.proto` files. +When present, this will cause `typealias`es to be added to the generated Swift +messages that replace the package name prefix with the provided string. For +example, the following `.proto` file: + +```protobuf +option swift_package_typealias = "FBP"; +package foo.bar; + +message Baz { + // Message fields +} +``` + +would generate the following Swift source: + +```swift +public struct Foo_Bar_Baz { + // Message fields and other methods +} + +typealias FBPBaz = Foo_Bar_Baz +``` + +It should be noted that this type alias is recorded in the generated +`.swiftmodule` so that code importing the module can refer to it, but it does +not cause a new symbol to be generated in the compiled binary (i.e., we do not +risk compiled size bloat by adding `typealias`es for every type). + +Other strategies to handle packages that were considered and rejected can be +found in [Appendix A](#appendix-a-rejected-strategies-to-handle-packages). + +## Messages + +Proto messages are natural value types and we will generate messages as structs +instead of classes. Users will benefit from Swift’s built-in behavior with +regard to mutability. We will define a `ProtoMessage` protocol that defines the +common methods and properties for all messages (such as serialization) and also +lets users treat messages polymorphically. Any shared method implementations +that do not differ between individual messages can be implemented in a protocol +extension. + +The backing storage itself for fields of a message will be managed by a +`ProtoFieldStorage` type that uses an internal dictionary keyed by field number, +and whose values are the value of the field with that number (up-cast to Swift’s +`Any` type). This class will provide type-safe getters and setters so that +generated messages can manipulate this storage, and core serialization logic +will live here as well. Furthermore, factoring the storage out into a separate +type, rather than inlining the fields as stored properties in the message +itself, lets us implement copy-on-write efficiently to support passing around +large messages. (Furthermore, because the messages themselves are value types, +inlining fields is not possible if the fields are submessages of the same type, +or a type that eventually includes a submessage of the same type.) + +### Required fields (proto2 only) + +Required fields in proto2 messages seem like they could be naturally represented +by non-optional properties in Swift, but this presents some problems/concerns. + +Serialization APIs permit partial serialization, which allows required fields to +remain unset. Furthermore, other language APIs still provide `has*` and `clear*` +methods for required fields, and knowing whether a property has a value when the +message is in memory is still useful. + +For example, an e-mail draft message may have the “to” address required on the +wire, but when the user constructs it in memory, it doesn’t make sense to force +a value until they provide one. We only want to force a value to be present when +the message is serialized to the wire. Using non-optional properties prevents +this use case, and makes client usage awkward because the user would be forced +to select a sentinel or placeholder value for any required fields at the time +the message was created. + +### Default values + +In proto2, fields can have a default value specified that may be a value other +than the default value for its corresponding language type (for example, a +default value of 5 instead of 0 for an integer). When reading a field that is +not explicitly set, the user expects to get that value. This makes Swift +optionals (i.e., `Foo?`) unsuitable for fields in general. Unfortunately, we +cannot implement our own “enhanced optional” type without severely complicating +usage (Swift’s use of type inference and its lack of implicit conversions would +require manual unwrapping of every property value). + +Instead, we can use **implicitly unwrapped optionals.** For example, a property +generated for a field of type `int32` would have Swift type `Int32!`. These +properties would behave with the following characteristics, which mirror the +nil-resettable properties used elsewhere in Apple’s SDKs (for example, +`UIView.tintColor`): + +* Assigning a non-nil value to a property sets the field to that value. +* Assigning nil to a property clears the field (its internal representation is + nilled out). +* Reading the value of a property returns its value if it is set, or returns + its default value if it is not set. Reading a property never returns nil. + +The final point in the list above implies that the optional cannot be checked to +determine if the field is set to a value other than its default: it will never +be nil. Instead, we must provide `has*` methods for each field to allow the user +to check this. These methods will be public in proto2. In proto3, these methods +will be private (if generated at all), since the user can test the returned +value against the zero value for that type. + +### Autocreation of nested messages + +For convenience, dotting into an unset field representing a nested message will +return an instance of that message with default values. As in the Objective-C +implementation, this does not actually cause the field to be set until the +returned message is mutated. Fortunately, thanks to the way mutability of value +types is implemented in Swift, the language automatically handles the +reassignment-on-mutation for us. A static singleton instance containing default +values can be associated with each message that can be returned when reading, so +copies are only made by the Swift runtime when mutation occurs. For example, +given the following proto: + +```protobuf +message Node { + Node child = 1; + string value = 2 [default = "foo"]; +} +``` + +The following Swift code would act as commented, where setting deeply nested +properties causes the copies and mutations to occur as the assignment statement +is unwound: + +```swift +var node = Node() + +let s = node.child.child.value +// 1. node.child returns the "default Node". +// 2. Reading .child on the result of (1) returns the same default Node. +// 3. Reading .value on the result of (2) returns the default value "foo". + +node.child.child.value = "bar" +// 4. Setting .value on the default Node causes a copy to be made and sets +// the property on that copy. Subsequently, the language updates the +// value of "node.child.child" to point to that copy. +// 5. Updating "node.child.child" in (4) requires another copy, because +// "node.child" was also the instance of the default node. The copy is +// assigned back to "node.child". +// 6. Setting "node.child" in (5) is a simple value reassignment, since +// "node" is a mutable var. +``` + +In other words, the generated messages do not internally have to manage parental +relationships to backfill the appropriate properties on mutation. Swift provides +this for free. + +## Scalar value fields + +Proto scalar value fields will map to Swift types in the following way: + +.proto Type | Swift Type +----------- | ------------------- +`double` | `Double` +`float` | `Float` +`int32` | `Int32` +`int64` | `Int64` +`uint32` | `UInt32` +`uint64` | `UInt64` +`sint32` | `Int32` +`sint64` | `Int64` +`fixed32` | `UInt32` +`fixed64` | `UInt64` +`sfixed32` | `Int32` +`sfixed64` | `Int64` +`bool` | `Bool` +`string` | `String` +`bytes` | `Foundation.NSData` + +The proto spec defines a number of integral types that map to the same Swift +type; for example, `intXX`, `sintXX`, and `sfixedXX` are all signed integers, +and `uintXX` and `fixedXX` are both unsigned integers. No other language +implementation distinguishes these further, so we do not do so either. The +rationale is that the various types only serve to distinguish how the value is +**encoded on the wire**; once loaded in memory, the user is not concerned about +these variations. + +Swift’s lack of implicit conversions among types will make it slightly annoying +to use these types in a context expecting an `Int`, or vice-versa, but since +this is a data-interchange format with explicitly-sized fields, we should not +hide that information from the user. Users will have to explicitly write +`Int(message.myField)`, for example. + +## Embedded message fields + +Embedded message fields can be represented using an optional variable of the +generated message type. Thus, the message + +```protobuf +message Foo { + Bar bar = 1; +} +``` + +would be represented in Swift as + +```swift +public struct Foo: ProtoMessage { + public var bar: Bar! { + get { ... } + set { ... } + } +} +``` + +If the user explicitly sets `bar` to nil, or if it was never set when read from +the wire, retrieving the value of `bar` would return a default, statically +allocated instance of `Bar` containing default values for its fields. This +achieves the desired behavior for default values in the same way that scalar +fields are designed, and also allows users to deep-drill into complex object +graphs to get or set fields without checking for nil at each step. + +## Enum fields + +The design and implementation of enum fields will differ somewhat drastically +depending on whether the message being generated is a proto2 or proto3 message. + +### proto2 enums + +For proto2, we do not need to be concerned about unknown enum values, so we can +use the simple raw-value enum syntax provided by Swift. So the following enum in +proto2: + +```protobuf +enum ContentType { + TEXT = 0; + IMAGE = 1; +} +``` + +would become this Swift enum: + +```swift +public enum ContentType: Int32, NilLiteralConvertible { + case text = 0 + case image = 1 + + public init(nilLiteral: ()) { + self = .text + } +} +``` + +See below for the discussion about `NilLiteralConvertible`. + +### proto3 enums + +For proto3, we need to be able to preserve unknown enum values that may come +across the wire so that they can be written back if unmodified. We can +accomplish this in Swift by using a case with an associated value for unknowns. +So the following enum in proto3: + +```protobuf +enum ContentType { + TEXT = 0; + IMAGE = 1; +} +``` + +would become this Swift enum: + +```swift +public enum ContentType: RawRepresentable, NilLiteralConvertible { + case text + case image + case UNKNOWN_VALUE(Int32) + + public typealias RawValue = Int32 + + public init(nilLiteral: ()) { + self = .text + } + + public init(rawValue: RawValue) { + switch rawValue { + case 0: self = .text + case 1: self = .image + default: self = .UNKNOWN_VALUE(rawValue) + } + + public var rawValue: RawValue { + switch self { + case .text: return 0 + case .image: return 1 + case .UNKNOWN_VALUE(let value): return value + } + } +} +``` + +Note that the use of a parameterized case prevents us from inheriting from the +raw `Int32` type; Swift does not allow an enum with a raw type to have cases +with arguments. Instead, we must implement the raw value initializer and +computed property manually. The `UNKNOWN_VALUE` case is explicitly chosen to be +"ugly" so that it stands out and does not conflict with other possible case +names. + +Using this approach, proto3 consumers must always have a default case or handle +the `.UNKNOWN_VALUE` case to satisfy case exhaustion in a switch statement; the +Swift compiler considers it an error if switch statements are not exhaustive. + +### NilLiteralConvertible conformance + +This is required to clean up the usage of enum-typed properties in switch +statements. Unlike other field types, enum properties cannot be +implicitly-unwrapped optionals without requiring that uses in switch statements +be explicitly unwrapped. For example, if we consider a message with the enum +above, this usage will fail to compile: + +```swift +// Without NilLiteralConvertible conformance on ContentType +public struct SomeMessage: ProtoMessage { + public var contentType: ContentType! { ... } +} + +// ERROR: no case named text or image +switch someMessage.contentType { + case .text: { ... } + case .image: { ... } +} +``` + +Even though our implementation guarantees that `contentType` will never be nil, +if it is an optional type, its cases would be `some` and `none`, not the cases +of the underlying enum type. In order to use it in this context, the user must +write `someMessage.contentType!` in their switch statement. + +Making the enum itself `NilLiteralConvertible` permits us to make the property +non-optional, so the user can still set it to nil to clear it (i.e., reset it to +its default value), while eliminating the need to explicitly unwrap it in a +switch statement. + +```swift +// With NilLiteralConvertible conformance on ContentType +public struct SomeMessage: ProtoMessage { + // Note that the property type is no longer optional + public var contentType: ContentType { ... } +} + +// OK: Compiles and runs as expected +switch someMessage.contentType { + case .text: { ... } + case .image: { ... } +} + +// The enum can be reset to its default value this way +someMessage.contentType = nil +``` + +One minor oddity with this approach is that nil will be auto-converted to the +default value of the enum in any context, not just field assignment. In other +words, this is valid: + +```swift +func foo(contentType: ContentType) { ... } +foo(nil) // Inside foo, contentType == .text +``` + +That being said, the advantage of being able to simultaneously support +nil-resettability and switch-without-unwrapping outweighs this side effect, +especially if appropriately documented. It is our hope that a new form of +resettable properties will be added to Swift that eliminates this inconsistency. +Some community members have already drafted or sent proposals for review that +would benefit our designs: + +* [SE-0030: Property Behaviors] + (https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md) +* [Drafted: Resettable Properties] + (https://github.com/patters/swift-evolution/blob/master/proposals/0000-resettable-properties.md) + +### Enum aliases + +The `allow_alias` option in protobuf slightly complicates the use of Swift enums +to represent that type, because raw values of cases in an enum must be unique. +Swift lets us define static variables in an enum that alias actual cases. For +example, the following protobuf enum: + +```protobuf +enum Foo { + option allow_alias = true; + BAR = 0; + BAZ = 0; +} +``` + +will be represented in Swift as: + +```swift +public enum Foo: Int32, NilLiteralConvertible { + case bar = 0 + static public let baz = bar + + // ... etc. +} + +// Can still use .baz shorthand to reference the alias in contexts +// where the type is inferred +``` + +That is, we use the first name as the actual case and use static variables for +the other aliases. One drawback to this approach is that the static aliases +cannot be used as cases in a switch statement (the compiler emits the error +*“Enum case ‘baz’ not found in type ‘Foo’”*). However, in our own code bases, +there are only a few places where enum aliases are not mere renamings of an +older value, but they also don’t appear to be the type of value that one would +expect to switch on (for example, a group of named constants representing +metrics rather than a set of options), so this restriction is not significant. + +This strategy also implies that changing the name of an enum and adding the old +name as an alias below the new name will be a breaking change in the generated +Swift code. + +## Oneof types + +The `oneof` feature represents a “variant/union” data type that maps nicely to +Swift enums with associated values (algebraic types). These fields can also be +accessed independently though, and, specifically in the case of proto2, it’s +reasonable to expect access to default values when accessing a field that is not +explicitly set. + +Taking all this into account, we can represent a `oneof` in Swift with two sets +of constructs: + +* Properties in the message that correspond to the `oneof` fields. +* A nested enum named after the `oneof` and which provides the corresponding + field values as case arguments. + +This approach fulfills the needs of proto consumers by providing a +Swift-idiomatic way of simultaneously checking which field is set and accessing +its value, providing individual properties to access the default values +(important for proto2), and safely allows a field to be moved into a `oneof` +without breaking clients. + +Consider the following proto: + +```protobuf +message MyMessage { + oneof record { + string name = 1 [default = "unnamed"]; + int32 id_number = 2 [default = 0]; + } +} +``` + +In Swift, we would generate an enum, a property for that enum, and properties +for the fields themselves: + +```swift +public struct MyMessage: ProtoMessage { + public enum Record: NilLiteralConvertible { + case name(String) + case idNumber(Int32) + case NOT_SET + + public init(nilLiteral: ()) { self = .NOT_SET } + } + + // This is the "Swifty" way of accessing the value + public var record: Record { ... } + + // Direct access to the underlying fields + public var name: String! { ... } + public var idNumber: Int32! { ... } +} +``` + +This makes both usage patterns possible: + +```swift +// Usage 1: Case-based dispatch +switch message.record { + case .name(let name): + // Do something with name if it was explicitly set + case .idNumber(let id): + // Do something with id_number if it was explicitly set + case .NOT_SET: + // Do something if it’s not set +} + +// Usage 2: Direct access for default value fallback +// Sets the label text to the name if it was explicitly set, or to +// "unnamed" (the default value for the field) if id_number was set +// instead +let myLabel = UILabel() +myLabel.text = message.name +``` + +As with proto enums, the generated `oneof` enum conforms to +`NilLiteralConvertible` to avoid switch statement issues. Setting the property +to nil will clear it (i.e., reset it to `NOT_SET`). + +## Unknown Fields (proto2 only) + +To be written. + +## Extensions (proto2 only) + +To be written. + +## Reflection and Descriptors + +We will not include reflection or descriptors in the first version of the Swift +library. The use cases for reflection on mobile are not as strong and the static +data to represent the descriptors would add bloat when we wish to keep the code +size small. + +In the future, we will investigate whether they can be included as extensions +which might be able to be excluded from a build and/or automatically dead +stripped by the compiler if they are not used. + +## Appendix A: Rejected strategies to handle packages + +### Each package is its own Swift module + +Each proto package could be declared as its own Swift module, replacing dots +with underscores (e.g., package `foo.bar` becomes module `Foo_Bar`). Then, users +would simply import modules containing whatever proto modules they want to use +and refer to the generated types by their short names. + +**This solution is simply not possible, however.** Swift modules cannot +circularly reference each other, but there is no restriction against proto +packages doing so. Circular imports are forbidden (e.g., `foo.proto` importing +`bar.proto` importing `foo.proto`), but nothing prevents package `foo` from +using a type in package `bar` which uses a different type in package `foo`, as +long as there is no import cycle. If these packages were generated as Swift +modules, then `Foo` would contain an `import Bar` statement and `Bar` would +contain an `import Foo` statement, and there is no way to compile this. + +### Ad hoc namespacing with structs + +We can “fake” namespaces in Swift by declaring empty structs with private +initializers. Since modules are constructed based on compiler arguments, not by +syntactic constructs, and because there is no pure Swift way to define +submodules (even though Clang module maps support this), there is no +source-drive way to group generated code into namespaces aside from this +approach. + +Types can be added to those intermediate package structs using Swift extensions. +For example, a message `Baz` in package `foo.bar` could be represented in Swift +as follows: + +```swift +public struct Foo { + private init() {} +} + +public extension Foo { + public struct Bar { + private init() {} + } +} + +public extension Foo.Bar { + public struct Baz { + // Message fields and other methods + } +} + +let baz = Foo.Bar.Baz() +``` + +Each of these constructs would actually be defined in a separate file; Swift +lets us keep them separate and add multiple structs to a single “namespace” +through extensions. + +Unfortunately, these intermediate structs generate symbols of their own +(metatype information in the data segment). This becomes problematic if multiple +build targets contain Swift sources generated from different messages in the +same package. At link time, these symbols would collide, resulting in multiple +definition errors. + +This approach also has the disadvantage that there is no automatic “short” way +to refer to the generated messages at the deepest nesting levels; since this use +of structs is a hack around the lack of namespaces, there is no equivalent to +import (Java) or using (C++) to simplify this. Users would have to declare type +aliases to make this cleaner, or we would have to generate them for users. diff --git a/docs/third_party.md b/docs/third_party.md new file mode 100644 index 000000000..69ed6a386 --- /dev/null +++ b/docs/third_party.md @@ -0,0 +1,147 @@ +# Third-Party Add-ons for Protocol Buffers + +This page lists code related to Protocol Buffers which is developed and maintained by third parties. You may find this code useful, but note that **these projects are not affiliated with or endorsed by Google (unless explicitly marked)**; try them at your own risk. Also note that many projects here are in the early stages of development and not production-ready. + +If you have a project that should be listed here, please [send us a pull request](https://github.com/google/protobuf/pulls) to update this page. + +## Programming Languages + +These are projects we know about implementing Protocol Buffers for other programming languages: +* Action Script: http://code.google.com/p/protobuf-actionscript3/ +* Action Script: https://code.google.com/p/protoc-gen-as3/ +* Action Script: https://github.com/matrix3d/JProtoc +* C: https://github.com/protobuf-c/protobuf-c +* C: http://koti.kapsi.fi/jpa/nanopb/ +* C: https://github.com/cloudwu/pbc/ +* C: https://github.com/haberman/upb/wiki +* C: https://github.com/squidfunk/protobluff +* C++: https://github.com/google/protobuf (Google-official implementation) +* C/C++: http://spbc.sf.net/ +* C#: http://code.google.com/p/protobuf-csharp-port +* C#: http://code.google.com/p/protosharp/ +* C#: https://silentorbit.com/protobuf/ +* C#/.NET/WCF/VB: http://code.google.com/p/protobuf-net/ +* Clojure: http://github.com/ninjudd/clojure-protobuf +* Common Lisp: http://www.prism.gatech.edu/~ndantam3/docs/s-protobuf/ +* Common Lisp: http://github.com/brown/protobuf +* D: https://github.com/msoucy/dproto +* D: http://256.makerslocal.org/wiki/index.php/ProtocolBuffer +* D: https://github.com/opticron/ProtocolBuffer +* Dart: https://github.com/dart-lang/dart-protobuf (runtime) https://github.com/dart-lang/dart-protoc-plugin (code generator) +* Delphi: http://sourceforge.net/projects/protobuf-delphi/ +* Delphi: http://fundementals.sourceforge.net/dl.html +* Elixir: https://github.com/jeremyong/exprotoc +* Erlang: http://github.com/ngerakines/erlang_protobuffs/tree/master +* Erlang: http://piqi.org/ +* Erlang: https://code.google.com/p/protoc-gen-erl/ +* Erlang: https://github.com/basho/erlang_protobuffs +* Go: https://github.com/golang/protobuf (Google-official implementation) +* Go: http://code.google.com/p/goprotobuf/ +* Go: https://github.com/akunspy/gopbuf +* Haskell: http://hackage.haskell.org/package/hprotoc +* Haxe: https://github.com/Atry/protoc-gen-haxe +* Java: https://github.com/google/protobuf (Google-official implementation) +* Java/Android: https://github.com/square/wire +* Java ME: http://code.google.com/p/protobuf-javame/ +* Java ME: http://swingme.sourceforge.net/encode.shtml +* Java ME: http://github.com/ponderingpanda/protobuf-j2me +* Java ME: http://code.google.com/p/protobuf-j2me/ +* Javascript: http://code.google.com/p/protobuf-js/ +* Javascript: http://github.com/sirikata/protojs +* Javascript: https://github.com/dcodeIO/ProtoBuf.js +* Javascript: http://code.google.com/p/protobuf-for-node/ +* Javascript: http://code.google.com/p/protostuff/ +* Julia: https://github.com/tanmaykm/ProtoBuf.jl +* Lua: http://code.google.com/p/protoc-gen-lua/ +* Lua: http://github.com/indygreg/lua-protobuf +* Lua: https://github.com/Neopallium/lua-pb +* Matlab: http://code.google.com/p/protobuf-matlab/ +* Mercury: http://code.google.com/p/protobuf-mercury/ +* Objective C: http://code.google.com/p/protobuf-objc/ +* Objective C: https://github.com/alexeyxo/protobuf-objc +* OCaml: http://piqi.org/ +* Perl: http://groups.google.com/group/protobuf-perl +* Perl: http://search.cpan.org/perldoc?Google::ProtocolBuffers +* Perl/XS: http://code.google.com/p/protobuf-perlxs/ +* PHP: http://code.google.com/p/pb4php/ +* PHP: https://github.com/allegro/php-protobuf/ +* PHP: https://github.com/chobie/php-protocolbuffers +* PHP: http://drslump.github.com/Protobuf-PHP +* Prolog: http://www.swi-prolog.org/pldoc/package/protobufs.html +* Python: https://github.com/google/protobuf (Google-official implementation) +* Python: http://eigenein.github.com/protobuf/ +* R: http://cran.r-project.org/package=RProtoBuf +* Ruby: http://code.google.com/p/ruby-protobuf/ +* Ruby: http://github.com/mozy/ruby-protocol-buffers +* Ruby: https://github.com/bmizerany/beefcake/tree/master/lib/beefcake +* Ruby: https://github.com/localshred/protobuf +* Rust: https://github.com/stepancheg/rust-protobuf/ +* Scala: http://github.com/jeffplaisance/scala-protobuf +* Scala: http://code.google.com/p/protobuf-scala +* Scala: https://github.com/SandroGrzicic/ScalaBuff +* Scala: http://trueaccord.github.io/ScalaPB/ +* Swift: https://github.com/alexeyxo/protobuf-swift +* Vala: https://launchpad.net/protobuf-vala +* Visual Basic: http://code.google.com/p/protobuf-net/ + +## RPC Implementations + +GRPC (http://www.grpc.io/) is Google's RPC implementation for Protocol Buffers. There are other third-party RPC implementations as well. Some of these actually work with Protocol Buffers service definitions (defined using the `service` keyword in `.proto` files) while others just use Protocol Buffers message objects. + +* https://github.com/grpc/grpc (C++, Node.js, Python, Ruby, Objective-C, PHP, C#, Google-official implementation) +* http://zeroc.com/ice.html (Multiple languages) +* http://code.google.com/p/protobuf-net/ (C#/.NET/WCF/VB) +* https://launchpad.net/txprotobuf/ (Python) +* https://github.com/modeswitch/protobuf-rpc (Python) +* http://code.google.com/p/protobuf-socket-rpc/ (Java, Python) +* http://code.google.com/p/proto-streamer/ (Java) +* http://code.google.com/p/server1/ (C++) +* http://deltavsoft.com/RcfUserGuide/Protobufs (C++) +* http://code.google.com/p/protobuf-mina-rpc/ (Python client, Java server) +* http://code.google.com/p/casocklib/ (C++) +* http://code.google.com/p/cxf-protobuf/ (Java) +* http://code.google.com/p/protobuf-remote/ (C++/C#) +* http://code.google.com/p/protobuf-rpc-pro/ (Java) +* https://code.google.com/p/protorpc/ (Go/C++) +* https://code.google.com/p/eneter-protobuf-serializer/ (Java/.NET) +* http://www.deltavsoft.com/RCFProto.html (C++/Java/Python/C#) +* https://github.com/robbinfan/claire-protorpc (C++) +* https://github.com/BaiduPS/sofa-pbrpc (C++) +* https://github.com/ebencheung/arab (C++) +* http://code.google.com/p/protobuf-csharp-rpc/ (C#) +* https://github.com/thesamet/rpcz (C++/Python, based on ZeroMQ) +* https://github.com/w359405949/libmaid (C++, Python) +* https://github.com/madwyn/libpbrpc (C++) + +## Other Utilities + +There are miscellaneous other things you may find useful as a Protocol Buffers developer. + +* [NetBeans IDE plugin](http://code.google.com/p/protobuf-netbeans-plugin/) +* [Wireshark/Ethereal packet sniffer plugin](http://code.google.com/p/protobuf-wireshark/) +* [Alternate encodings (JSON, XML, HTML) for Java protobufs](http://code.google.com/p/protobuf-java-format/) +* [Another JSON encoder/decoder for Java](https://github.com/sijuv/protobuf-codec) +* [Editor for serialized protobufs](http://code.google.com/p/protobufeditor/) +* [Intellij IDEA plugin](http://github.com/nnmatveev/idea-plugin-protobuf) +* [TextMate syntax highlighting](http://github.com/michaeledgar/protobuf-tmbundle) +* [Oracle PL SQL plugin](http://code.google.com/p/protocol-buffer-plsql/) +* [Eclipse editor for protobuf (from Google)](http://code.google.com/p/protobuf-dt/) +* [C++ Builder compatible protobuf](https://github.com/saadware/protobuf-cppbuilder) +* Maven Protocol Compiler Plugin + * https://github.com/sergei-ivanov/maven-protoc-plugin/ + * http://igor-petruk.github.com/protobuf-maven-plugin/ + * http://code.google.com/p/maven-protoc-plugin/ + * https://github.com/os72/protoc-jar-maven-plugin +* [Documentation generator plugin (Markdown/HTML/DocBook/...)](https://github.com/estan/protoc-gen-doc) +* [DocBook generator for .proto files](http://code.google.com/p/protoc-gen-docbook/) +* [Protobuf for nginx module](https://github.com/dbcode/protobuf-nginx/) +* [RSpec matchers and Cucumber step defs for testing Protocol Buffers](https://github.com/connamara/protobuf_spec) +* [Sbt plugin for Protocol Buffers](https://github.com/Atry/sbt-cppp) +* [Gradle Protobuf Plugin](https://github.com/aantono/gradle-plugin-protobuf) +* [Multi-platform executable JAR and Java API for protoc](https://github.com/os72/protoc-jar) +* [Python scripts to convert between Protocol Buffers and JSON](https://github.com/NextTuesday/py-pb-converters) +* [Visual Studio Language Service support for Protocol Buffers](http://visualstudiogallery.msdn.microsoft.com/4bc0f38c-b058-4e05-ae38-155e053c19c5) +* [C++ library for serialization/de-serialization between Protocol Buffers and JSON.](https://github.com/yinqiwen/pbjson) +* [ProtoBuf with Java EE7 Expression Language 3.0; pure Java ProtoBuf Parser and Builder.](https://github.com/protobufel/protobuf-el) +* [Notepad++ Syntax Highlighting for .proto files](https://github.com/chai2010/notepadplus-protobuf) +* [Linter for .proto files](https://github.com/ckaznocha/protoc-gen-lint) diff --git a/generate_descriptor_proto.sh b/generate_descriptor_proto.sh index 81b8a0d64..c170c837e 100755 --- a/generate_descriptor_proto.sh +++ b/generate_descriptor_proto.sh @@ -10,8 +10,6 @@ # to make when building protoc. This is particularly useful for passing # -j4 to run 4 jobs simultaneously. -set -e - if test ! -e src/google/protobuf/stubs/common.h; then cat >&2 << __EOF__ Could not find source code. Make sure you are running this script from the @@ -52,9 +50,14 @@ do echo "Round $PROCESS_ROUND" CORE_PROTO_IS_CORRECT=1 - make $@ protoc && - ./protoc --cpp_out=dllexport_decl=LIBPROTOBUF_EXPORT:$TMP ${RUNTIME_PROTO_FILES[@]} && \ - ./protoc --cpp_out=dllexport_decl=LIBPROTOC_EXPORT:$TMP google/protobuf/compiler/plugin.proto + make $@ protoc + if test $? -ne 0; then + echo "Failed to build protoc." + exit 1 + fi + + ./protoc --cpp_out=dllexport_decl=LIBPROTOBUF_EXPORT:$TMP ${RUNTIME_PROTO_FILES[@]} && \ + ./protoc --cpp_out=dllexport_decl=LIBPROTOC_EXPORT:$TMP google/protobuf/compiler/plugin.proto for PROTO_FILE in ${RUNTIME_PROTO_FILES[@]}; do BASE_NAME=${PROTO_FILE%.*} @@ -92,9 +95,9 @@ do done cd .. -if test -x objectivec/generate_descriptors_proto.sh; then +if test -x objectivec/generate_well_known_types.sh; then echo "Generating messages for objc." - objectivec/generate_descriptors_proto.sh $@ + objectivec/generate_well_known_types.sh $@ fi if test -x csharp/generate_protos.sh; then diff --git a/java/core/src/main/java/com/google/protobuf/AbstractMessage.java b/java/core/src/main/java/com/google/protobuf/AbstractMessage.java index 9f418f2b9..03c0d5797 100644 --- a/java/core/src/main/java/com/google/protobuf/AbstractMessage.java +++ b/java/core/src/main/java/com/google/protobuf/AbstractMessage.java @@ -50,17 +50,23 @@ import java.util.Map; * * @author kenton@google.com Kenton Varda */ -public abstract class AbstractMessage extends AbstractMessageLite - implements Message { +public abstract class AbstractMessage + // TODO(dweis): Update GeneratedMessage to parameterize with MessageType and BuilderType. + extends AbstractMessageLite + implements Message { + + @Override public boolean isInitialized() { return MessageReflection.isInitialized(this); } + @Override public List findInitializationErrors() { return MessageReflection.findMissingFields(this); } + @Override public String getInitializationErrorString() { return MessageReflection.delimitWithCommas(findInitializationErrors()); } @@ -83,12 +89,14 @@ public abstract class AbstractMessage extends AbstractMessageLite return TextFormat.printToString(this); } + @Override public void writeTo(final CodedOutputStream output) throws IOException { MessageReflection.writeMessageTo(this, getAllFields(), output, false); } protected int memoizedSize = -1; + @Override public int getSerializedSize() { int size = memoizedSize; if (size != -1) { @@ -288,8 +296,8 @@ public abstract class AbstractMessage extends AbstractMessageLite * other methods. */ @SuppressWarnings("unchecked") - public static abstract class Builder - extends AbstractMessageLite.Builder + public static abstract class Builder> + extends AbstractMessageLite.Builder implements Message.Builder { // The compiler produces an error if this is not declared explicitly. @Override @@ -314,6 +322,7 @@ public abstract class AbstractMessage extends AbstractMessageLite throw new UnsupportedOperationException("clearOneof() is not implemented."); } + @Override public BuilderType clear() { for (final Map.Entry entry : getAllFields().entrySet()) { @@ -322,14 +331,22 @@ public abstract class AbstractMessage extends AbstractMessageLite return (BuilderType) this; } + @Override public List findInitializationErrors() { return MessageReflection.findMissingFields(this); } + @Override public String getInitializationErrorString() { return MessageReflection.delimitWithCommas(findInitializationErrors()); } + + @Override + protected BuilderType internalMergeFrom(AbstractMessageLite other) { + return mergeFrom((Message) other); + } + @Override public BuilderType mergeFrom(final Message other) { if (other.getDescriptorForType() != getDescriptorForType()) { throw new IllegalArgumentException( @@ -407,6 +424,7 @@ public abstract class AbstractMessage extends AbstractMessageLite return (BuilderType) this; } + @Override public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) { setUnknownFields( UnknownFieldSet.newBuilder(getUnknownFields()) @@ -415,17 +433,19 @@ public abstract class AbstractMessage extends AbstractMessageLite return (BuilderType) this; } + @Override public Message.Builder getFieldBuilder(final FieldDescriptor field) { throw new UnsupportedOperationException( "getFieldBuilder() called on an unsupported message type."); } - public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, - int index) { + @Override + public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, int index) { throw new UnsupportedOperationException( "getRepeatedFieldBuilder() called on an unsupported message type."); } + @Override public String toString() { return TextFormat.printToString(this); } @@ -462,7 +482,7 @@ public abstract class AbstractMessage extends AbstractMessageLite @Override public BuilderType mergeFrom(final ByteString data) throws InvalidProtocolBufferException { - return super.mergeFrom(data); + return (BuilderType) super.mergeFrom(data); } @Override @@ -470,20 +490,20 @@ public abstract class AbstractMessage extends AbstractMessageLite final ByteString data, final ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { - return super.mergeFrom(data, extensionRegistry); + return (BuilderType) super.mergeFrom(data, extensionRegistry); } @Override public BuilderType mergeFrom(final byte[] data) throws InvalidProtocolBufferException { - return super.mergeFrom(data); + return (BuilderType) super.mergeFrom(data); } @Override public BuilderType mergeFrom( final byte[] data, final int off, final int len) throws InvalidProtocolBufferException { - return super.mergeFrom(data, off, len); + return (BuilderType) super.mergeFrom(data, off, len); } @Override @@ -491,7 +511,7 @@ public abstract class AbstractMessage extends AbstractMessageLite final byte[] data, final ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { - return super.mergeFrom(data, extensionRegistry); + return (BuilderType) super.mergeFrom(data, extensionRegistry); } @Override @@ -499,13 +519,13 @@ public abstract class AbstractMessage extends AbstractMessageLite final byte[] data, final int off, final int len, final ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { - return super.mergeFrom(data, off, len, extensionRegistry); + return (BuilderType) super.mergeFrom(data, off, len, extensionRegistry); } @Override public BuilderType mergeFrom(final InputStream input) throws IOException { - return super.mergeFrom(input); + return (BuilderType) super.mergeFrom(input); } @Override @@ -513,7 +533,7 @@ public abstract class AbstractMessage extends AbstractMessageLite final InputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException { - return super.mergeFrom(input, extensionRegistry); + return (BuilderType) super.mergeFrom(input, extensionRegistry); } @Override diff --git a/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java b/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java index 12384983e..43736dd10 100644 --- a/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java +++ b/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java @@ -43,9 +43,13 @@ import java.util.Collection; * * @author kenton@google.com Kenton Varda */ -public abstract class AbstractMessageLite implements MessageLite { +public abstract class AbstractMessageLite< + MessageType extends AbstractMessageLite, + BuilderType extends AbstractMessageLite.Builder> + implements MessageLite { protected int memoizedHashCode = 0; - + + @Override public ByteString toByteString() { try { final ByteString.CodedBuilder out = @@ -59,6 +63,7 @@ public abstract class AbstractMessageLite implements MessageLite { } } + @Override public byte[] toByteArray() { try { final byte[] result = new byte[getSerializedSize()]; @@ -73,6 +78,7 @@ public abstract class AbstractMessageLite implements MessageLite { } } + @Override public void writeTo(final OutputStream output) throws IOException { final int bufferSize = CodedOutputStream.computePreferredBufferSize(getSerializedSize()); @@ -82,6 +88,7 @@ public abstract class AbstractMessageLite implements MessageLite { codedOutput.flush(); } + @Override public void writeDelimitedTo(final OutputStream output) throws IOException { final int serialized = getSerializedSize(); final int bufferSize = CodedOutputStream.computePreferredBufferSize( @@ -120,25 +127,27 @@ public abstract class AbstractMessageLite implements MessageLite { * other methods. */ @SuppressWarnings("unchecked") - public static abstract class Builder + public abstract static class Builder< + MessageType extends AbstractMessageLite, + BuilderType extends Builder> implements MessageLite.Builder { // The compiler produces an error if this is not declared explicitly. @Override public abstract BuilderType clone(); - public BuilderType mergeFrom(final CodedInputStream input) - throws IOException { + @Override + public BuilderType mergeFrom(final CodedInputStream input) throws IOException { return mergeFrom(input, ExtensionRegistryLite.getEmptyRegistry()); } // Re-defined here for return type covariance. + @Override public abstract BuilderType mergeFrom( - final CodedInputStream input, - final ExtensionRegistryLite extensionRegistry) + final CodedInputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException; - public BuilderType mergeFrom(final ByteString data) - throws InvalidProtocolBufferException { + @Override + public BuilderType mergeFrom(final ByteString data) throws InvalidProtocolBufferException { try { final CodedInputStream input = data.newCodedInput(); mergeFrom(input); @@ -153,9 +162,9 @@ public abstract class AbstractMessageLite implements MessageLite { } } + @Override public BuilderType mergeFrom( - final ByteString data, - final ExtensionRegistryLite extensionRegistry) + final ByteString data, final ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { try { final CodedInputStream input = data.newCodedInput(); @@ -171,14 +180,14 @@ public abstract class AbstractMessageLite implements MessageLite { } } - public BuilderType mergeFrom(final byte[] data) - throws InvalidProtocolBufferException { + @Override + public BuilderType mergeFrom(final byte[] data) throws InvalidProtocolBufferException { return mergeFrom(data, 0, data.length); } - public BuilderType mergeFrom(final byte[] data, final int off, - final int len) - throws InvalidProtocolBufferException { + @Override + public BuilderType mergeFrom(final byte[] data, final int off, final int len) + throws InvalidProtocolBufferException { try { final CodedInputStream input = CodedInputStream.newInstance(data, off, len); @@ -194,15 +203,17 @@ public abstract class AbstractMessageLite implements MessageLite { } } - public BuilderType mergeFrom( - final byte[] data, - final ExtensionRegistryLite extensionRegistry) + @Override + public BuilderType mergeFrom(final byte[] data, final ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { return mergeFrom(data, 0, data.length, extensionRegistry); } + @Override public BuilderType mergeFrom( - final byte[] data, final int off, final int len, + final byte[] data, + final int off, + final int len, final ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { try { @@ -220,6 +231,7 @@ public abstract class AbstractMessageLite implements MessageLite { } } + @Override public BuilderType mergeFrom(final InputStream input) throws IOException { final CodedInputStream codedInput = CodedInputStream.newInstance(input); mergeFrom(codedInput); @@ -227,10 +239,9 @@ public abstract class AbstractMessageLite implements MessageLite { return (BuilderType) this; } + @Override public BuilderType mergeFrom( - final InputStream input, - final ExtensionRegistryLite extensionRegistry) - throws IOException { + final InputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException { final CodedInputStream codedInput = CodedInputStream.newInstance(input); mergeFrom(codedInput, extensionRegistry); codedInput.checkLastTagWas(0); @@ -292,10 +303,9 @@ public abstract class AbstractMessageLite implements MessageLite { } } + @Override public boolean mergeDelimitedFrom( - final InputStream input, - final ExtensionRegistryLite extensionRegistry) - throws IOException { + final InputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException { final int firstByte = input.read(); if (firstByte == -1) { return false; @@ -306,11 +316,24 @@ public abstract class AbstractMessageLite implements MessageLite { return true; } - public boolean mergeDelimitedFrom(final InputStream input) - throws IOException { + @Override + public boolean mergeDelimitedFrom(final InputStream input) throws IOException { return mergeDelimitedFrom(input, ExtensionRegistryLite.getEmptyRegistry()); } + + @Override + @SuppressWarnings("unchecked") // isInstance takes care of this + public BuilderType mergeFrom(final MessageLite other) { + if (!getDefaultInstanceForType().getClass().isInstance(other)) { + throw new IllegalArgumentException( + "mergeFrom(MessageLite) can only merge messages of the same type."); + } + + return internalMergeFrom((MessageType) other); + } + + protected abstract BuilderType internalMergeFrom(MessageType message); /** * Construct an UninitializedMessageException reporting missing fields in diff --git a/java/core/src/main/java/com/google/protobuf/AbstractParser.java b/java/core/src/main/java/com/google/protobuf/AbstractParser.java index 1a4c63110..66b0ee3bd 100644 --- a/java/core/src/main/java/com/google/protobuf/AbstractParser.java +++ b/java/core/src/main/java/com/google/protobuf/AbstractParser.java @@ -78,26 +78,27 @@ public abstract class AbstractParser private static final ExtensionRegistryLite EMPTY_REGISTRY = ExtensionRegistryLite.getEmptyRegistry(); + @Override public MessageType parsePartialFrom(CodedInputStream input) throws InvalidProtocolBufferException { return parsePartialFrom(input, EMPTY_REGISTRY); } - public MessageType parseFrom(CodedInputStream input, - ExtensionRegistryLite extensionRegistry) + @Override + public MessageType parseFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { return checkMessageInitialized( parsePartialFrom(input, extensionRegistry)); } - public MessageType parseFrom(CodedInputStream input) - throws InvalidProtocolBufferException { + @Override + public MessageType parseFrom(CodedInputStream input) throws InvalidProtocolBufferException { return parseFrom(input, EMPTY_REGISTRY); } - public MessageType parsePartialFrom(ByteString data, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { + @Override + public MessageType parsePartialFrom(ByteString data, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { MessageType message; try { CodedInputStream input = data.newCodedInput(); @@ -113,24 +114,25 @@ public abstract class AbstractParser } } - public MessageType parsePartialFrom(ByteString data) - throws InvalidProtocolBufferException { + @Override + public MessageType parsePartialFrom(ByteString data) throws InvalidProtocolBufferException { return parsePartialFrom(data, EMPTY_REGISTRY); } - public MessageType parseFrom(ByteString data, - ExtensionRegistryLite extensionRegistry) + @Override + public MessageType parseFrom(ByteString data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { return checkMessageInitialized(parsePartialFrom(data, extensionRegistry)); } - public MessageType parseFrom(ByteString data) - throws InvalidProtocolBufferException { + @Override + public MessageType parseFrom(ByteString data) throws InvalidProtocolBufferException { return parseFrom(data, EMPTY_REGISTRY); } - public MessageType parsePartialFrom(byte[] data, int off, int len, - ExtensionRegistryLite extensionRegistry) + @Override + public MessageType parsePartialFrom( + byte[] data, int off, int len, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { try { CodedInputStream input = CodedInputStream.newInstance(data, off, len); @@ -146,47 +148,50 @@ public abstract class AbstractParser } } + @Override public MessageType parsePartialFrom(byte[] data, int off, int len) throws InvalidProtocolBufferException { return parsePartialFrom(data, off, len, EMPTY_REGISTRY); } - public MessageType parsePartialFrom(byte[] data, - ExtensionRegistryLite extensionRegistry) + @Override + public MessageType parsePartialFrom(byte[] data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { return parsePartialFrom(data, 0, data.length, extensionRegistry); } - public MessageType parsePartialFrom(byte[] data) - throws InvalidProtocolBufferException { + @Override + public MessageType parsePartialFrom(byte[] data) throws InvalidProtocolBufferException { return parsePartialFrom(data, 0, data.length, EMPTY_REGISTRY); } - public MessageType parseFrom(byte[] data, int off, int len, - ExtensionRegistryLite extensionRegistry) + @Override + public MessageType parseFrom( + byte[] data, int off, int len, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { return checkMessageInitialized( parsePartialFrom(data, off, len, extensionRegistry)); } + @Override public MessageType parseFrom(byte[] data, int off, int len) throws InvalidProtocolBufferException { return parseFrom(data, off, len, EMPTY_REGISTRY); } - public MessageType parseFrom(byte[] data, - ExtensionRegistryLite extensionRegistry) + @Override + public MessageType parseFrom(byte[] data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { return parseFrom(data, 0, data.length, extensionRegistry); } - public MessageType parseFrom(byte[] data) - throws InvalidProtocolBufferException { + @Override + public MessageType parseFrom(byte[] data) throws InvalidProtocolBufferException { return parseFrom(data, EMPTY_REGISTRY); } - public MessageType parsePartialFrom(InputStream input, - ExtensionRegistryLite extensionRegistry) + @Override + public MessageType parsePartialFrom(InputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { CodedInputStream codedInput = CodedInputStream.newInstance(input); MessageType message = parsePartialFrom(codedInput, extensionRegistry); @@ -198,26 +203,26 @@ public abstract class AbstractParser return message; } - public MessageType parsePartialFrom(InputStream input) - throws InvalidProtocolBufferException { + @Override + public MessageType parsePartialFrom(InputStream input) throws InvalidProtocolBufferException { return parsePartialFrom(input, EMPTY_REGISTRY); } - public MessageType parseFrom(InputStream input, - ExtensionRegistryLite extensionRegistry) + @Override + public MessageType parseFrom(InputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { return checkMessageInitialized( parsePartialFrom(input, extensionRegistry)); } - public MessageType parseFrom(InputStream input) - throws InvalidProtocolBufferException { + @Override + public MessageType parseFrom(InputStream input) throws InvalidProtocolBufferException { return parseFrom(input, EMPTY_REGISTRY); } + @Override public MessageType parsePartialDelimitedFrom( - InputStream input, - ExtensionRegistryLite extensionRegistry) + InputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { int size; try { @@ -233,21 +238,21 @@ public abstract class AbstractParser return parsePartialFrom(limitedInput, extensionRegistry); } + @Override public MessageType parsePartialDelimitedFrom(InputStream input) throws InvalidProtocolBufferException { return parsePartialDelimitedFrom(input, EMPTY_REGISTRY); } - public MessageType parseDelimitedFrom( - InputStream input, - ExtensionRegistryLite extensionRegistry) + @Override + public MessageType parseDelimitedFrom(InputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { return checkMessageInitialized( parsePartialDelimitedFrom(input, extensionRegistry)); } - public MessageType parseDelimitedFrom(InputStream input) - throws InvalidProtocolBufferException { + @Override + public MessageType parseDelimitedFrom(InputStream input) throws InvalidProtocolBufferException { return parseDelimitedFrom(input, EMPTY_REGISTRY); } } diff --git a/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java b/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java index bb6446b2a..b17db6e0b 100644 --- a/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java +++ b/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java @@ -34,19 +34,25 @@ import com.google.protobuf.Internal.ProtobufList; import java.util.AbstractList; import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; /** * An abstract implementation of {@link ProtobufList} which manages mutability semantics. All mutate - * methods are check if the list is mutable before proceeding. Subclasses must invoke + * methods must check if the list is mutable before proceeding. Subclasses must invoke * {@link #ensureIsMutable()} manually when overriding those methods. + *

+ * This implementation assumes all subclasses are array based, supporting random access. */ abstract class AbstractProtobufList extends AbstractList implements ProtobufList { + protected static final int DEFAULT_CAPACITY = 10; + /** * Whether or not this list is modifiable. */ private boolean isMutable; - + /** * Constructs a mutable list by default. */ @@ -54,6 +60,44 @@ abstract class AbstractProtobufList extends AbstractList implements Protob isMutable = true; } + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof List)) { + return false; + } + // Handle lists that do not support RandomAccess as efficiently as possible by using an iterator + // based approach in our super class. Otherwise our index based approach will avoid those + // allocations. + if (!(o instanceof RandomAccess)) { + return super.equals(o); + } + + List other = (List) o; + final int size = size(); + if (size != other.size()) { + return false; + } + for (int i = 0; i < size; i++) { + if (!get(i).equals(other.get(i))) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + final int size = size(); + int hashCode = 1; + for (int i = 0; i < size; i++) { + hashCode = (31 * hashCode) + get(i).hashCode(); + } + return hashCode; + } + @Override public boolean add(E e) { ensureIsMutable(); diff --git a/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java b/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java index 70e042f5d..8b2820b6e 100644 --- a/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java +++ b/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java @@ -34,7 +34,6 @@ import com.google.protobuf.Internal.BooleanList; import java.util.Arrays; import java.util.Collection; -import java.util.List; import java.util.RandomAccess; /** @@ -45,8 +44,6 @@ import java.util.RandomAccess; final class BooleanArrayList extends AbstractProtobufList implements BooleanList, RandomAccess { - private static final int DEFAULT_CAPACITY = 10; - private static final BooleanArrayList EMPTY_LIST = new BooleanArrayList(); static { EMPTY_LIST.makeImmutable(); @@ -60,7 +57,7 @@ final class BooleanArrayList * The backing store for the list. */ private boolean[] array; - + /** * The size of the list distinct from the length of the array. That is, it is the number of * elements set in the list. @@ -71,35 +68,57 @@ final class BooleanArrayList * Constructs a new mutable {@code BooleanArrayList} with default capacity. */ BooleanArrayList() { - this(DEFAULT_CAPACITY); + this(new boolean[DEFAULT_CAPACITY], 0); } /** - * Constructs a new mutable {@code BooleanArrayList} with the provided capacity. + * Constructs a new mutable {@code BooleanArrayList}. */ - BooleanArrayList(int capacity) { - array = new boolean[capacity]; - size = 0; - } - - /** - * Constructs a new mutable {@code BooleanArrayList} containing the same elements as - * {@code other}. - */ - BooleanArrayList(List other) { - if (other instanceof BooleanArrayList) { - BooleanArrayList list = (BooleanArrayList) other; - array = list.array.clone(); - size = list.size; - } else { - size = other.size(); - array = new boolean[size]; - for (int i = 0; i < size; i++) { - array[i] = other.get(i); - } - } + private BooleanArrayList(boolean[] array, int size) { + this.array = array; + this.size = size; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BooleanArrayList)) { + return super.equals(o); + } + BooleanArrayList other = (BooleanArrayList) o; + if (size != other.size) { + return false; + } + + final boolean[] arr = other.array; + for (int i = 0; i < size; i++) { + if (array[i] != arr[i]) { + return false; + } + } + + return true; + } + + @Override + public int hashCode() { + int result = 1; + for (int i = 0; i < size; i++) { + result = (31 * result) + Internal.hashBoolean(array[i]); + } + return result; + } + + @Override + public BooleanList mutableCopyWithCapacity(int capacity) { + if (capacity < size) { + throw new IllegalArgumentException(); + } + return new BooleanArrayList(Arrays.copyOf(array, capacity), size); + } + @Override public Boolean get(int index) { return getBoolean(index); diff --git a/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java b/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java new file mode 100644 index 000000000..0cc38175f --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java @@ -0,0 +1,145 @@ +// 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. + +package com.google.protobuf; + +import static java.lang.Math.max; +import static java.lang.Math.min; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.ref.SoftReference; +import java.nio.ByteBuffer; + +/** + * Utility class to provide efficient writing of {@link ByteBuffer}s to {@link OutputStream}s. + */ +final class ByteBufferWriter { + private ByteBufferWriter() {} + + /** + * Minimum size for a cached buffer. This prevents us from allocating buffers that are too + * small to be easily reused. + */ + // TODO(nathanmittler): tune this property or allow configuration? + private static final int MIN_CACHED_BUFFER_SIZE = 1024; + + /** + * Maximum size for a cached buffer. If a larger buffer is required, it will be allocated + * but not cached. + */ + // TODO(nathanmittler): tune this property or allow configuration? + private static final int MAX_CACHED_BUFFER_SIZE = 16 * 1024; + + /** + * The fraction of the requested buffer size under which the buffer will be reallocated. + */ + // TODO(nathanmittler): tune this property or allow configuration? + private static final float BUFFER_REALLOCATION_THRESHOLD = 0.5f; + + /** + * Keeping a soft reference to a thread-local buffer. This buffer is used for writing a + * {@link ByteBuffer} to an {@link OutputStream} when no zero-copy alternative was available. + * Using a "soft" reference since VMs may keep this reference around longer than "weak" + * (e.g. HotSpot will maintain soft references until memory pressure warrants collection). + */ + private static final ThreadLocal> BUFFER = + new ThreadLocal>(); + + /** + * For testing purposes only. Clears the cached buffer to force a new allocation on the next + * invocation. + */ + static void clearCachedBuffer() { + BUFFER.set(null); + } + + /** + * Writes the remaining content of the buffer to the given stream. The buffer {@code position} + * will remain unchanged by this method. + */ + static void write(ByteBuffer buffer, OutputStream output) throws IOException { + final int initialPos = buffer.position(); + try { + if (buffer.hasArray()) { + // Optimized write for array-backed buffers. + // Note that we're taking the risk that a malicious OutputStream could modify the array. + output.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining()); + } else if (output instanceof FileOutputStream) { + // Use a channel to write out the ByteBuffer. This will automatically empty the buffer. + ((FileOutputStream) output).getChannel().write(buffer); + } else { + // Read all of the data from the buffer to an array. + // TODO(nathanmittler): Consider performance improvements for other "known" stream types. + final byte[] array = getOrCreateBuffer(buffer.remaining()); + while (buffer.hasRemaining()) { + int length = min(buffer.remaining(), array.length); + buffer.get(array, 0, length); + output.write(array, 0, length); + } + } + } finally { + // Restore the initial position. + buffer.position(initialPos); + } + } + + private static byte[] getOrCreateBuffer(int requestedSize) { + requestedSize = max(requestedSize, MIN_CACHED_BUFFER_SIZE); + + byte[] buffer = getBuffer(); + // Only allocate if we need to. + if (buffer == null || needToReallocate(requestedSize, buffer.length)) { + buffer = new byte[requestedSize]; + + // Only cache the buffer if it's not too big. + if (requestedSize <= MAX_CACHED_BUFFER_SIZE) { + setBuffer(buffer); + } + } + return buffer; + } + + private static boolean needToReallocate(int requestedSize, int bufferLength) { + // First check against just the requested length to avoid the multiply. + return bufferLength < requestedSize + && bufferLength < requestedSize * BUFFER_REALLOCATION_THRESHOLD; + } + + private static byte[] getBuffer() { + SoftReference sr = BUFFER.get(); + return sr == null ? null : sr.get(); + } + + private static void setBuffer(byte[] value) { + BUFFER.set(new SoftReference(value)); + } +} diff --git a/java/core/src/main/java/com/google/protobuf/ByteOutput.java b/java/core/src/main/java/com/google/protobuf/ByteOutput.java new file mode 100644 index 000000000..8b7b04c8c --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/ByteOutput.java @@ -0,0 +1,116 @@ +// 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. + +package com.google.protobuf; + +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * An output target for raw bytes. This interface provides semantics that support two types of + * writing: + * + *

Traditional write operations: + * (as defined by {@link java.io.OutputStream}) where the target method is responsible for either + * copying the data or completing the write before returning from the method call. + * + *

Lazy write operations: where the caller guarantees that it will never modify the + * provided buffer and it can therefore be considered immutable. The target method is free to + * maintain a reference to the buffer beyond the scope of the method call (e.g. until the write + * operation completes). + */ +@ExperimentalApi +public abstract class ByteOutput { + /** + * Writes a single byte. + * + * @param value the byte to be written + * @throws IOException thrown if an error occurred while writing + */ + public abstract void write(byte value) throws IOException; + + /** + * Writes a sequence of bytes. The {@link ByteOutput} must copy {@code value} if it will + * not be processed prior to the return of this method call, since {@code value} may be + * reused/altered by the caller. + * + *

NOTE: This method MUST NOT modify the {@code value}. Doing so is a + * programming error and will lead to data corruption which will be difficult to debug. + * + * @param value the bytes to be written + * @param offset the offset of the start of the writable range + * @param length the number of bytes to write starting from {@code offset} + * @throws IOException thrown if an error occurred while writing + */ + public abstract void write(byte[] value, int offset, int length) throws IOException; + + /** + * Writes a sequence of bytes. The {@link ByteOutput} is free to retain a reference to the value + * beyond the scope of this method call (e.g. write later) since it is considered immutable and is + * guaranteed not to change by the caller. + * + *

NOTE: This method MUST NOT modify the {@code value}. Doing so is a + * programming error and will lead to data corruption which will be difficult to debug. + * + * @param value the bytes to be written + * @param offset the offset of the start of the writable range + * @param length the number of bytes to write starting from {@code offset} + * @throws IOException thrown if an error occurred while writing + */ + public abstract void writeLazy(byte[] value, int offset, int length) throws IOException; + + /** + * Writes a sequence of bytes. The {@link ByteOutput} must copy {@code value} if it will + * not be processed prior to the return of this method call, since {@code value} may be + * reused/altered by the caller. + * + *

NOTE: This method MUST NOT modify the {@code value}. Doing so is a + * programming error and will lead to data corruption which will be difficult to debug. + * + * @param value the bytes to be written. Upon returning from this call, the {@code position} of + * this buffer will be set to the {@code limit} + * @throws IOException thrown if an error occurred while writing + */ + public abstract void write(ByteBuffer value) throws IOException; + + /** + * Writes a sequence of bytes. The {@link ByteOutput} is free to retain a reference to the value + * beyond the scope of this method call (e.g. write later) since it is considered immutable and is + * guaranteed not to change by the caller. + * + *

NOTE: This method MUST NOT modify the {@code value}. Doing so is a + * programming error and will lead to data corruption which will be difficult to debug. + * + * @param value the bytes to be written. Upon returning from this call, the {@code position} of + * this buffer will be set to the {@code limit} + * @throws IOException thrown if an error occurred while writing + */ + public abstract void writeLazy(ByteBuffer value) throws IOException; +} diff --git a/java/core/src/main/java/com/google/protobuf/ByteString.java b/java/core/src/main/java/com/google/protobuf/ByteString.java index 305236f3d..62c945085 100644 --- a/java/core/src/main/java/com/google/protobuf/ByteString.java +++ b/java/core/src/main/java/com/google/protobuf/ByteString.java @@ -1,4 +1,32 @@ -// Copyright 2007 Google Inc. All rights reserved. +// 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. package com.google.protobuf; @@ -15,6 +43,7 @@ import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -58,6 +87,54 @@ public abstract class ByteString implements Iterable, Serializable { * Empty {@code ByteString}. */ public static final ByteString EMPTY = new LiteralByteString(Internal.EMPTY_BYTE_ARRAY); + + /** + * An interface to efficiently copy {@code byte[]}. + * + *

One of the noticable costs of copying a byte[] into a new array using + * {@code System.arraycopy} is nullification of a new buffer before the copy. It has been shown + * the Hotspot VM is capable to intrisicfy {@code Arrays.copyOfRange} operation to avoid this + * expensive nullification and provide substantial performance gain. Unfortunately this does not + * hold on Android runtimes and could make the copy slightly slower due to additional code in + * the {@code Arrays.copyOfRange}. Thus we provide two different implementation for array copier + * for Hotspot and Android runtimes. + */ + private interface ByteArrayCopier { + /** + * Copies the specified range of the specified array into a new array + */ + byte[] copyFrom(byte[] bytes, int offset, int size); + } + + /** Implementation of {@code ByteArrayCopier} which uses {@link System#arraycopy}. */ + private static final class SystemByteArrayCopier implements ByteArrayCopier { + @Override + public byte[] copyFrom(byte[] bytes, int offset, int size) { + byte[] copy = new byte[size]; + System.arraycopy(bytes, offset, copy, 0, size); + return copy; + } + } + + /** Implementation of {@code ByteArrayCopier} which uses {@link Arrays#copyOfRange}. */ + private static final class ArraysByteArrayCopier implements ByteArrayCopier { + @Override + public byte[] copyFrom(byte[] bytes, int offset, int size) { + return Arrays.copyOfRange(bytes, offset, offset + size); + } + } + + private static final ByteArrayCopier byteArrayCopier; + static { + boolean isAndroid = true; + try { + Class.forName("android.content.Context"); + } catch (ClassNotFoundException e) { + isAndroid = false; + } + + byteArrayCopier = isAndroid ? new SystemByteArrayCopier() : new ArraysByteArrayCopier(); + } /** * Cached hash value. Intentionally accessed via a data race, which @@ -77,7 +154,7 @@ public abstract class ByteString implements Iterable, Serializable { * * @param index index of byte * @return the value - * @throws ArrayIndexOutOfBoundsException {@code index < 0 or index >= size} + * @throws IndexOutOfBoundsException {@code index < 0 or index >= size} */ public abstract byte byteAt(int index); @@ -109,7 +186,7 @@ public abstract class ByteString implements Iterable, Serializable { public byte nextByte() { try { return byteAt(position++); - } catch (ArrayIndexOutOfBoundsException e) { + } catch (IndexOutOfBoundsException e) { throw new NoSuchElementException(e.getMessage()); } } @@ -220,9 +297,7 @@ public abstract class ByteString implements Iterable, Serializable { * @return new {@code ByteString} */ public static ByteString copyFrom(byte[] bytes, int offset, int size) { - byte[] copy = new byte[size]; - System.arraycopy(bytes, offset, copy, 0, size); - return new LiteralByteString(copy); + return new LiteralByteString(byteArrayCopier.copyFrom(bytes, offset, size)); } /** @@ -559,12 +634,7 @@ public abstract class ByteString implements Iterable, Serializable { } /** - * Writes the complete contents of this byte string to - * the specified output stream argument. - * - *

It is assumed that the {@link OutputStream} will not modify the contents passed it - * it. It may be possible for a malicious {@link OutputStream} to corrupt - * the data underlying the {@link ByteString}. + * Writes a copy of the contents of this byte string to the specified output stream argument. * * @param out the output stream to which to write the data. * @throws IOException if an I/O error occurs. @@ -578,8 +648,7 @@ public abstract class ByteString implements Iterable, Serializable { * @param sourceOffset offset within these bytes * @param numberToWrite number of bytes to write * @throws IOException if an I/O error occurs. - * @throws IndexOutOfBoundsException if an offset or size is negative or too - * large + * @throws IndexOutOfBoundsException if an offset or size is negative or too large */ final void writeTo(OutputStream out, int sourceOffset, int numberToWrite) throws IOException { @@ -596,6 +665,20 @@ public abstract class ByteString implements Iterable, Serializable { abstract void writeToInternal(OutputStream out, int sourceOffset, int numberToWrite) throws IOException; + /** + * Writes this {@link ByteString} to the provided {@link ByteOutput}. Calling + * this method may result in multiple operations on the target {@link ByteOutput}. + * + *

This method may expose internal backing buffers of the {@link ByteString} to the {@link + * ByteOutput} in order to avoid additional copying overhead. It would be possible for a malicious + * {@link ByteOutput} to corrupt the {@link ByteString}. Use with caution! + * + * @param byteOutput the output target to receive the bytes + * @throws IOException if an I/O error occurs + * @see UnsafeByteOperations#unsafeWriteTo(ByteString, ByteOutput) + */ + abstract void writeTo(ByteOutput byteOutput) throws IOException; + /** * Constructs a read-only {@code java.nio.ByteBuffer} whose content * is equal to the contents of this byte string. @@ -1102,7 +1185,7 @@ public abstract class ByteString implements Iterable, Serializable { * * @param index the index position to be tested * @param size the length of the array - * @throws ArrayIndexOutOfBoundsException if the index does not fall within the array. + * @throws IndexOutOfBoundsException if the index does not fall within the array. */ static void checkIndex(int index, int size) { if ((index | (size - (index + 1))) < 0) { @@ -1120,7 +1203,7 @@ public abstract class ByteString implements Iterable, Serializable { * @param endIndex the end index of the range (exclusive) * @param size the size of the array. * @return the length of the range. - * @throws ArrayIndexOutOfBoundsException some or all of the range falls outside of the array. + * @throws IndexOutOfBoundsException some or all of the range falls outside of the array. */ static int checkRange(int startIndex, int endIndex, int size) { final int length = endIndex - startIndex; @@ -1235,6 +1318,11 @@ public abstract class ByteString implements Iterable, Serializable { outputStream.write(bytes, getOffsetIntoBytes() + sourceOffset, numberToWrite); } + @Override + final void writeTo(ByteOutput output) throws IOException { + output.writeLazy(bytes, getOffsetIntoBytes(), size()); + } + @Override protected final String toStringInternal(Charset charset) { return new String(bytes, getOffsetIntoBytes(), size(), charset); diff --git a/java/core/src/main/java/com/google/protobuf/CodedInputStream.java b/java/core/src/main/java/com/google/protobuf/CodedInputStream.java index b3118ee01..e8860651a 100644 --- a/java/core/src/main/java/com/google/protobuf/CodedInputStream.java +++ b/java/core/src/main/java/com/google/protobuf/CodedInputStream.java @@ -55,7 +55,14 @@ public final class CodedInputStream { * Create a new CodedInputStream wrapping the given InputStream. */ public static CodedInputStream newInstance(final InputStream input) { - return new CodedInputStream(input); + return new CodedInputStream(input, BUFFER_SIZE); + } + + /** + * Create a new CodedInputStream wrapping the given InputStream. + */ + static CodedInputStream newInstance(final InputStream input, int bufferSize) { + return new CodedInputStream(input, bufferSize); } /** @@ -70,14 +77,14 @@ public final class CodedInputStream { */ public static CodedInputStream newInstance(final byte[] buf, final int off, final int len) { - return newInstance(buf, off, len, false); + return newInstance(buf, off, len, false /* bufferIsImmutable */); } - + /** * Create a new CodedInputStream wrapping the given byte array slice. */ - public static CodedInputStream newInstance(final byte[] buf, final int off, - final int len, boolean bufferIsImmutable) { + static CodedInputStream newInstance( + final byte[] buf, final int off, final int len, final boolean bufferIsImmutable) { CodedInputStream result = new CodedInputStream(buf, off, len, bufferIsImmutable); try { // Some uses of CodedInputStream can be more efficient if they know @@ -361,6 +368,11 @@ public final class CodedInputStream { return result; } else if (size == 0) { return ""; + } else if (size <= bufferSize) { + refillBuffer(size); + String result = new String(buffer, bufferPos, size, Internal.UTF_8); + bufferPos += size; + return result; } else { // Slow path: Build a byte array first then copy it. return new String(readRawBytesSlowPath(size), Internal.UTF_8); @@ -375,14 +387,21 @@ public final class CodedInputStream { public String readStringRequireUtf8() throws IOException { final int size = readRawVarint32(); final byte[] bytes; - int pos = bufferPos; - if (size <= (bufferSize - pos) && size > 0) { + final int oldPos = bufferPos; + final int pos; + if (size <= (bufferSize - oldPos) && size > 0) { // Fast path: We already have the bytes in a contiguous buffer, so // just copy directly from it. bytes = buffer; - bufferPos = pos + size; + bufferPos = oldPos + size; + pos = oldPos; } else if (size == 0) { return ""; + } else if (size <= bufferSize) { + refillBuffer(size); + bytes = buffer; + pos = 0; + bufferPos = pos + size; } else { // Slow path: Build a byte array first then copy it. bytes = readRawBytesSlowPath(size); @@ -869,7 +888,8 @@ public final class CodedInputStream { private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB private static final int BUFFER_SIZE = 4096; - private CodedInputStream(final byte[] buffer, final int off, final int len, boolean bufferIsImmutable) { + private CodedInputStream( + final byte[] buffer, final int off, final int len, boolean bufferIsImmutable) { this.buffer = buffer; bufferSize = off + len; bufferPos = off; @@ -878,8 +898,8 @@ public final class CodedInputStream { this.bufferIsImmutable = bufferIsImmutable; } - private CodedInputStream(final InputStream input) { - buffer = new byte[BUFFER_SIZE]; + private CodedInputStream(final InputStream input, int bufferSize) { + buffer = new byte[bufferSize]; bufferSize = 0; bufferPos = 0; totalBytesRetired = 0; diff --git a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java index d8ebad217..ad174d0ff 100644 --- a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java +++ b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java @@ -30,11 +30,18 @@ package com.google.protobuf; +import static java.lang.Math.max; + import com.google.protobuf.Utf8.UnpairedSurrogateException; import java.io.IOException; import java.io.OutputStream; +import java.lang.reflect.Field; +import java.nio.BufferOverflowException; import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; import java.util.logging.Level; import java.util.logging.Logger; @@ -49,20 +56,22 @@ import java.util.logging.Logger; * you are writing some other format of your own design, use the latter. * *

This class is totally unsynchronized. - * - * @author kneton@google.com Kenton Varda */ -public final class CodedOutputStream { - +public abstract class CodedOutputStream extends ByteOutput { private static final Logger logger = Logger.getLogger(CodedOutputStream.class.getName()); + private static final sun.misc.Unsafe UNSAFE = getUnsafe(); + private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = supportsUnsafeArrayOperations(); + private static final long ARRAY_BASE_OFFSET = byteArrayBaseOffset(); - // TODO(dweis): Consider migrating to a ByteBuffer. - private final byte[] buffer; - private final int limit; - private int position; - private int totalBytesWritten = 0; + private static final int FIXED_32_SIZE = 4; + private static final int FIXED_64_SIZE = 8; + private static final int MAX_VARINT_SIZE = 10; - private final OutputStream output; + /** + * @deprecated Use {@link #computeFixed32SizeNoTag(int)} instead. + */ + @Deprecated + public static final int LITTLE_ENDIAN_32_SIZE = FIXED_32_SIZE; /** * The buffer size used in {@link #newInstance(OutputStream)}. @@ -77,40 +86,33 @@ public final class CodedOutputStream { * CodedOutputStream. */ static int computePreferredBufferSize(int dataLength) { - if (dataLength > DEFAULT_BUFFER_SIZE) return DEFAULT_BUFFER_SIZE; + if (dataLength > DEFAULT_BUFFER_SIZE) { + return DEFAULT_BUFFER_SIZE; + } return dataLength; } - private CodedOutputStream(final byte[] buffer, final int offset, - final int length) { - output = null; - this.buffer = buffer; - position = offset; - limit = offset + length; - } - - private CodedOutputStream(final OutputStream output, final byte[] buffer) { - this.output = output; - this.buffer = buffer; - position = 0; - limit = buffer.length; - } - /** - * Create a new {@code CodedOutputStream} wrapping the given - * {@code OutputStream}. + * Create a new {@code CodedOutputStream} wrapping the given {@code OutputStream}. + * + *

NOTE: The provided {@link OutputStream} MUST NOT retain access or + * modify the provided byte arrays. Doing so may result in corrupted data, which would be + * difficult to debug. */ public static CodedOutputStream newInstance(final OutputStream output) { return newInstance(output, DEFAULT_BUFFER_SIZE); } /** - * Create a new {@code CodedOutputStream} wrapping the given - * {@code OutputStream} with a given buffer size. + * Create a new {@code CodedOutputStream} wrapping the given {@code OutputStream} with a given + * buffer size. + * + *

NOTE: The provided {@link OutputStream} MUST NOT retain access or + * modify the provided byte arrays. Doing so may result in corrupted data, which would be + * difficult to debug. */ - public static CodedOutputStream newInstance(final OutputStream output, - final int bufferSize) { - return new CodedOutputStream(output, new byte[bufferSize]); + public static CodedOutputStream newInstance(final OutputStream output, final int bufferSize) { + return new OutputStreamEncoder(output, bufferSize); } /** @@ -131,149 +133,146 @@ public final class CodedOutputStream { * array is faster than writing to an {@code OutputStream}. See also * {@link ByteString#newCodedBuilder}. */ - public static CodedOutputStream newInstance(final byte[] flatArray, - final int offset, - final int length) { - return new CodedOutputStream(flatArray, offset, length); + public static CodedOutputStream newInstance( + final byte[] flatArray, final int offset, final int length) { + return new ArrayEncoder(flatArray, offset, length); } /** - * Create a new {@code CodedOutputStream} that writes to the given ByteBuffer. + * Create a new {@code CodedOutputStream} that writes to the given {@link ByteBuffer}. */ public static CodedOutputStream newInstance(ByteBuffer byteBuffer) { - return newInstance(byteBuffer, DEFAULT_BUFFER_SIZE); + if (byteBuffer.hasArray()) { + return new NioHeapEncoder(byteBuffer); + } + return new NioEncoder(byteBuffer); } /** - * Create a new {@code CodedOutputStream} that writes to the given ByteBuffer. + * Create a new {@code CodedOutputStream} that writes to the given {@link ByteBuffer}. + * + * @deprecated the size parameter is no longer used since use of an internal buffer is useless + * (and wasteful) when writing to a {@link ByteBuffer}. Use {@link #newInstance(ByteBuffer)} + * instead. */ + @Deprecated public static CodedOutputStream newInstance(ByteBuffer byteBuffer, - int bufferSize) { - return newInstance(new ByteBufferOutputStream(byteBuffer), bufferSize); + @SuppressWarnings("unused") int unused) { + return newInstance(byteBuffer); } - private static class ByteBufferOutputStream extends OutputStream { - private final ByteBuffer byteBuffer; - public ByteBufferOutputStream(ByteBuffer byteBuffer) { - this.byteBuffer = byteBuffer; + /** + * Create a new {@code CodedOutputStream} that writes to the provided {@link ByteOutput}. + * + *

NOTE: The {@link ByteOutput} MUST NOT modify the provided buffers. Doing + * so may result in corrupted data, which would be difficult to debug. + * + * @param byteOutput the output target for encoded bytes. + * @param bufferSize the size of the internal scratch buffer to be used for string encoding. + * Setting this to {@code 0} will disable buffering, requiring an allocation for each encoded + * string. + */ + static CodedOutputStream newInstance(ByteOutput byteOutput, int bufferSize) { + if (bufferSize < 0) { + throw new IllegalArgumentException("bufferSize must be positive"); } - @Override - public void write(int b) throws IOException { - byteBuffer.put((byte) b); - } + return new ByteOutputEncoder(byteOutput, bufferSize); + } - @Override - public void write(byte[] data, int offset, int length) throws IOException { - byteBuffer.put(data, offset, length); - } + // Disallow construction outside of this class. + private CodedOutputStream() { } // ----------------------------------------------------------------- - /** Write a {@code double} field, including tag, to the stream. */ - public void writeDouble(final int fieldNumber, final double value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); - writeDoubleNoTag(value); - } - - /** Write a {@code float} field, including tag, to the stream. */ - public void writeFloat(final int fieldNumber, final float value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); - writeFloatNoTag(value); - } - - /** Write a {@code uint64} field, including tag, to the stream. */ - public void writeUInt64(final int fieldNumber, final long value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeUInt64NoTag(value); - } - - /** Write an {@code int64} field, including tag, to the stream. */ - public void writeInt64(final int fieldNumber, final long value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeInt64NoTag(value); - } + /** Encode and write a tag. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeTag(int fieldNumber, int wireType) throws IOException; /** Write an {@code int32} field, including tag, to the stream. */ - public void writeInt32(final int fieldNumber, final int value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeInt32NoTag(value); - } + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeInt32(int fieldNumber, int value) throws IOException; - /** Write a {@code fixed64} field, including tag, to the stream. */ - public void writeFixed64(final int fieldNumber, final long value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); - writeFixed64NoTag(value); + /** Write a {@code uint32} field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeUInt32(int fieldNumber, int value) throws IOException; + + /** Write a {@code sint32} field, including tag, to the stream. */ + public final void writeSInt32(final int fieldNumber, final int value) throws IOException { + writeUInt32(fieldNumber, encodeZigZag32(value)); } /** Write a {@code fixed32} field, including tag, to the stream. */ - public void writeFixed32(final int fieldNumber, final int value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); - writeFixed32NoTag(value); + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeFixed32(int fieldNumber, int value) throws IOException; + + /** Write an {@code sfixed32} field, including tag, to the stream. */ + public final void writeSFixed32(final int fieldNumber, final int value) throws IOException { + writeFixed32(fieldNumber, value); + } + + /** Write an {@code int64} field, including tag, to the stream. */ + public final void writeInt64(final int fieldNumber, final long value) throws IOException { + writeUInt64(fieldNumber, value); + } + + /** Write a {@code uint64} field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeUInt64(int fieldNumber, long value) throws IOException; + + /** Write an {@code sint64} field, including tag, to the stream. */ + public final void writeSInt64(final int fieldNumber, final long value) throws IOException { + writeUInt64(fieldNumber, encodeZigZag64(value)); + } + + /** Write a {@code fixed64} field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeFixed64(int fieldNumber, long value) throws IOException; + + /** Write an {@code sfixed64} field, including tag, to the stream. */ + public final void writeSFixed64(final int fieldNumber, final long value) throws IOException { + writeFixed64(fieldNumber, value); + } + + /** Write a {@code float} field, including tag, to the stream. */ + public final void writeFloat(final int fieldNumber, final float value) throws IOException { + writeFixed32(fieldNumber, Float.floatToRawIntBits(value)); + } + + /** Write a {@code double} field, including tag, to the stream. */ + public final void writeDouble(final int fieldNumber, final double value) throws IOException { + writeFixed64(fieldNumber, Double.doubleToRawLongBits(value)); } /** Write a {@code bool} field, including tag, to the stream. */ - public void writeBool(final int fieldNumber, final boolean value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeBoolNoTag(value); + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeBool(int fieldNumber, boolean value) throws IOException; + + /** + * Write an enum field, including tag, to the stream. The provided value is the numeric + * value used to represent the enum value on the wire (not the enum ordinal value). + */ + public final void writeEnum(final int fieldNumber, final int value) throws IOException { + writeInt32(fieldNumber, value); } /** Write a {@code string} field, including tag, to the stream. */ - public void writeString(final int fieldNumber, final String value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - writeStringNoTag(value); - } - - /** Write a {@code group} field, including tag, to the stream. */ - public void writeGroup(final int fieldNumber, final MessageLite value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); - writeGroupNoTag(value); - writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); - } - - - /** Write an embedded message field, including tag, to the stream. */ - public void writeMessage(final int fieldNumber, final MessageLite value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - writeMessageNoTag(value); - } - + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeString(int fieldNumber, String value) throws IOException; /** Write a {@code bytes} field, including tag, to the stream. */ - public void writeBytes(final int fieldNumber, final ByteString value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - writeBytesNoTag(value); - } + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeBytes(int fieldNumber, ByteString value) throws IOException; /** Write a {@code bytes} field, including tag, to the stream. */ - public void writeByteArray(final int fieldNumber, final byte[] value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - writeByteArrayNoTag(value); - } + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeByteArray(int fieldNumber, byte[] value) throws IOException; /** Write a {@code bytes} field, including tag, to the stream. */ - public void writeByteArray(final int fieldNumber, - final byte[] value, - final int offset, - final int length) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - writeByteArrayNoTag(value, offset, length); - } + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeByteArray(int fieldNumber, byte[] value, int offset, int length) + throws IOException; /** * Write a {@code bytes} field, including tag, to the stream. @@ -285,326 +284,182 @@ public final class CodedOutputStream { * of a ByteBuffer, you can call * {@code writeByteBuffer(fieldNumber, byteBuffer.slice())}. */ - public void writeByteBuffer(final int fieldNumber, final ByteBuffer value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - writeByteBufferNoTag(value); + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeByteBuffer(int fieldNumber, ByteBuffer value) throws IOException; + + /** + * Write a single byte. + */ + public final void writeRawByte(final byte value) throws IOException { + write(value); } - /** Write a {@code uint32} field, including tag, to the stream. */ - public void writeUInt32(final int fieldNumber, final int value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeUInt32NoTag(value); + /** Write a single byte, represented by an integer value. */ + public final void writeRawByte(final int value) throws IOException { + write((byte) value); + } + + /** Write an array of bytes. */ + public final void writeRawBytes(final byte[] value) throws IOException { + write(value, 0, value.length); } /** - * Write an enum field, including tag, to the stream. Caller is responsible - * for converting the enum value to its numeric value. + * Write part of an array of bytes. */ - public void writeEnum(final int fieldNumber, final int value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeEnumNoTag(value); + public final void writeRawBytes(final byte[] value, int offset, int length) throws IOException { + write(value, offset, length); } - /** Write an {@code sfixed32} field, including tag, to the stream. */ - public void writeSFixed32(final int fieldNumber, final int value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); - writeSFixed32NoTag(value); + /** Write a byte string. */ + public final void writeRawBytes(final ByteString value) throws IOException { + value.writeTo(this); } - /** Write an {@code sfixed64} field, including tag, to the stream. */ - public void writeSFixed64(final int fieldNumber, final long value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); - writeSFixed64NoTag(value); - } + /** + * Write a ByteBuffer. This method will write all content of the ByteBuffer + * regardless of the current position and limit (i.e., the number of bytes + * to be written is value.capacity(), not value.remaining()). Furthermore, + * this method doesn't alter the state of the passed-in ByteBuffer. Its + * position, limit, mark, etc. will remain unchanged. If you only want to + * write the remaining bytes of a ByteBuffer, you can call + * {@code writeRawBytes(byteBuffer.slice())}. + */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeRawBytes(final ByteBuffer value) throws IOException; - /** Write an {@code sint32} field, including tag, to the stream. */ - public void writeSInt32(final int fieldNumber, final int value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeSInt32NoTag(value); - } - - /** Write an {@code sint64} field, including tag, to the stream. */ - public void writeSInt64(final int fieldNumber, final long value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeSInt64NoTag(value); - } + /** Write an embedded message field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeMessage(final int fieldNumber, final MessageLite value) + throws IOException; /** * Write a MessageSet extension field to the stream. For historical reasons, * the wire format differs from normal fields. */ - public void writeMessageSetExtension(final int fieldNumber, - final MessageLite value) - throws IOException { - writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); - writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); - writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); - writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); - } + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeMessageSetExtension(final int fieldNumber, final MessageLite value) + throws IOException; /** * Write an unparsed MessageSet extension field to the stream. For * historical reasons, the wire format differs from normal fields. */ - public void writeRawMessageSetExtension(final int fieldNumber, - final ByteString value) - throws IOException { - writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); - writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); - writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value); - writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); - } + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeRawMessageSetExtension(final int fieldNumber, final ByteString value) + throws IOException; // ----------------------------------------------------------------- - /** Write a {@code double} field to the stream. */ - public void writeDoubleNoTag(final double value) throws IOException { - writeRawLittleEndian64(Double.doubleToRawLongBits(value)); - } - - /** Write a {@code float} field to the stream. */ - public void writeFloatNoTag(final float value) throws IOException { - writeRawLittleEndian32(Float.floatToRawIntBits(value)); - } - - /** Write a {@code uint64} field to the stream. */ - public void writeUInt64NoTag(final long value) throws IOException { - writeRawVarint64(value); - } - - /** Write an {@code int64} field to the stream. */ - public void writeInt64NoTag(final long value) throws IOException { - writeRawVarint64(value); - } - /** Write an {@code int32} field to the stream. */ - public void writeInt32NoTag(final int value) throws IOException { - if (value >= 0) { - writeRawVarint32(value); - } else { - // Must sign-extend. - writeRawVarint64(value); - } - } + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeInt32NoTag(final int value) throws IOException; - /** Write a {@code fixed64} field to the stream. */ - public void writeFixed64NoTag(final long value) throws IOException { - writeRawLittleEndian64(value); + /** Write a {@code uint32} field to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeUInt32NoTag(int value) throws IOException; + + /** Write a {@code sint32} field to the stream. */ + public final void writeSInt32NoTag(final int value) throws IOException { + writeUInt32NoTag(encodeZigZag32(value)); } /** Write a {@code fixed32} field to the stream. */ - public void writeFixed32NoTag(final int value) throws IOException { - writeRawLittleEndian32(value); + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeFixed32NoTag(int value) throws IOException; + + /** Write a {@code sfixed32} field to the stream. */ + public final void writeSFixed32NoTag(final int value) throws IOException { + writeFixed32NoTag(value); + } + + /** Write an {@code int64} field to the stream. */ + public final void writeInt64NoTag(final long value) throws IOException { + writeUInt64NoTag(value); + } + + /** Write a {@code uint64} field to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeUInt64NoTag(long value) throws IOException; + + /** Write a {@code sint64} field to the stream. */ + public final void writeSInt64NoTag(final long value) throws IOException { + writeUInt64NoTag(encodeZigZag64(value)); + } + + /** Write a {@code fixed64} field to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeFixed64NoTag(long value) throws IOException; + + /** Write a {@code sfixed64} field to the stream. */ + public final void writeSFixed64NoTag(final long value) throws IOException { + writeFixed64NoTag(value); + } + + /** Write a {@code float} field to the stream. */ + public final void writeFloatNoTag(final float value) throws IOException { + writeFixed32NoTag(Float.floatToRawIntBits(value)); + } + + /** Write a {@code double} field to the stream. */ + public final void writeDoubleNoTag(final double value) throws IOException { + writeFixed64NoTag(Double.doubleToRawLongBits(value)); } /** Write a {@code bool} field to the stream. */ - public void writeBoolNoTag(final boolean value) throws IOException { - writeRawByte(value ? 1 : 0); + public final void writeBoolNoTag(final boolean value) throws IOException { + write((byte) (value ? 1 : 0)); + } + + /** + * Write an enum field to the stream. The provided value is the numeric + * value used to represent the enum value on the wire (not the enum ordinal value). + */ + public final void writeEnumNoTag(final int value) throws IOException { + writeInt32NoTag(value); } /** Write a {@code string} field to the stream. */ // TODO(dweis): Document behavior on ill-formed UTF-16 input. - public void writeStringNoTag(final String value) throws IOException { - try { - efficientWriteStringNoTag(value); - } catch (UnpairedSurrogateException e) { - logger.log(Level.WARNING, - "Converting ill-formed UTF-16. Your Protocol Buffer will not round trip correctly!", e); - inefficientWriteStringNoTag(value); - } + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeStringNoTag(String value) throws IOException; + + /** Write a {@code bytes} field to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeBytesNoTag(final ByteString value) throws IOException; + + /** Write a {@code bytes} field to the stream. */ + public final void writeByteArrayNoTag(final byte[] value) throws IOException { + writeByteArrayNoTag(value, 0, value.length); } - /** Write a {@code string} field to the stream. */ - private void inefficientWriteStringNoTag(final String value) throws IOException { - // Unfortunately there does not appear to be any way to tell Java to encode - // UTF-8 directly into our buffer, so we have to let it create its own byte - // array and then copy. - // TODO(dweis): Consider using nio Charset methods instead. - final byte[] bytes = value.getBytes(Internal.UTF_8); - writeRawVarint32(bytes.length); - writeRawBytes(bytes); - } - - /** - * Write a {@code string} field to the stream efficiently. If the {@code string} is malformed, - * this method rolls back its changes and throws an {@link UnpairedSurrogateException} with the - * intent that the caller will catch and retry with {@link #inefficientWriteStringNoTag(String)}. - * - * @param value the string to write to the stream - * - * @throws UnpairedSurrogateException when {@code value} is ill-formed UTF-16. - */ - private void efficientWriteStringNoTag(final String value) throws IOException { - // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()), - // and at most 3 times of it. We take advantage of this in both branches below. - final int maxLength = value.length() * Utf8.MAX_BYTES_PER_CHAR; - final int maxLengthVarIntSize = computeRawVarint32Size(maxLength); - - // If we are streaming and the potential length is too big to fit in our buffer, we take the - // slower path. Otherwise, we're good to try the fast path. - if (output != null && maxLengthVarIntSize + maxLength > limit - position) { - // Allocate a byte[] that we know can fit the string and encode into it. String.getBytes() - // does the same internally and then does *another copy* to return a byte[] of exactly the - // right size. We can skip that copy and just writeRawBytes up to the actualLength of the - // UTF-8 encoded bytes. - final byte[] encodedBytes = new byte[maxLength]; - int actualLength = Utf8.encode(value, encodedBytes, 0, maxLength); - writeRawVarint32(actualLength); - writeRawBytes(encodedBytes, 0, actualLength); - } else { - // Optimize for the case where we know this length results in a constant varint length as this - // saves a pass for measuring the length of the string. - final int minLengthVarIntSize = computeRawVarint32Size(value.length()); - int oldPosition = position; - final int length; - try { - if (minLengthVarIntSize == maxLengthVarIntSize) { - position = oldPosition + minLengthVarIntSize; - int newPosition = Utf8.encode(value, buffer, position, limit - position); - // Since this class is stateful and tracks the position, we rewind and store the state, - // prepend the length, then reset it back to the end of the string. - position = oldPosition; - length = newPosition - oldPosition - minLengthVarIntSize; - writeRawVarint32(length); - position = newPosition; - } else { - length = Utf8.encodedLength(value); - writeRawVarint32(length); - position = Utf8.encode(value, buffer, position, limit - position); - } - } catch (UnpairedSurrogateException e) { - // Be extra careful and restore the original position for retrying the write with the less - // efficient path. - position = oldPosition; - throw e; - } catch (ArrayIndexOutOfBoundsException e) { - throw new OutOfSpaceException(e); - } - totalBytesWritten += length; - } - } - - /** Write a {@code group} field to the stream. */ - public void writeGroupNoTag(final MessageLite value) throws IOException { - value.writeTo(this); - } - - /** Write an embedded message field to the stream. */ - public void writeMessageNoTag(final MessageLite value) throws IOException { - writeRawVarint32(value.getSerializedSize()); - value.writeTo(this); - } + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeMessageNoTag(final MessageLite value) throws IOException; + //================================================================= - /** Write a {@code bytes} field to the stream. */ - public void writeBytesNoTag(final ByteString value) throws IOException { - writeRawVarint32(value.size()); - writeRawBytes(value); - } + @ExperimentalApi + @Override + public abstract void write(byte value) throws IOException; - /** Write a {@code bytes} field to the stream. */ - public void writeByteArrayNoTag(final byte[] value) throws IOException { - writeRawVarint32(value.length); - writeRawBytes(value); - } + @ExperimentalApi + @Override + public abstract void write(byte[] value, int offset, int length) throws IOException; - /** Write a {@code bytes} field to the stream. */ - public void writeByteArrayNoTag(final byte[] value, - final int offset, - final int length) throws IOException { - writeRawVarint32(length); - writeRawBytes(value, offset, length); - } + @ExperimentalApi + @Override + public abstract void writeLazy(byte[] value, int offset, int length) throws IOException; - /** - * Write a {@code bytes} field to the stream. This method will write all - * content of the ByteBuffer regardless of the current position and limit - * (i.e., the number of bytes to be written is value.capacity(), not - * value.remaining()). Furthermore, this method doesn't alter the state of - * the passed-in ByteBuffer. Its position, limit, mark, etc. will remain - * unchanged. If you only want to write the remaining bytes of a ByteBuffer, - * you can call {@code writeByteBufferNoTag(byteBuffer.slice())}. - */ - public void writeByteBufferNoTag(final ByteBuffer value) throws IOException { - writeRawVarint32(value.capacity()); - writeRawBytes(value); - } + @Override + public abstract void write(ByteBuffer value) throws IOException; - /** Write a {@code uint32} field to the stream. */ - public void writeUInt32NoTag(final int value) throws IOException { - writeRawVarint32(value); - } - - /** - * Write an enum field to the stream. Caller is responsible - * for converting the enum value to its numeric value. - */ - public void writeEnumNoTag(final int value) throws IOException { - writeInt32NoTag(value); - } - - /** Write an {@code sfixed32} field to the stream. */ - public void writeSFixed32NoTag(final int value) throws IOException { - writeRawLittleEndian32(value); - } - - /** Write an {@code sfixed64} field to the stream. */ - public void writeSFixed64NoTag(final long value) throws IOException { - writeRawLittleEndian64(value); - } - - /** Write an {@code sint32} field to the stream. */ - public void writeSInt32NoTag(final int value) throws IOException { - writeRawVarint32(encodeZigZag32(value)); - } - - /** Write an {@code sint64} field to the stream. */ - public void writeSInt64NoTag(final long value) throws IOException { - writeRawVarint64(encodeZigZag64(value)); - } + @ExperimentalApi + @Override + public abstract void writeLazy(ByteBuffer value) throws IOException; // ================================================================= - - /** - * Compute the number of bytes that would be needed to encode a - * {@code double} field, including tag. - */ - public static int computeDoubleSize(final int fieldNumber, - final double value) { - return computeTagSize(fieldNumber) + computeDoubleSizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code float} field, including tag. - */ - public static int computeFloatSize(final int fieldNumber, final float value) { - return computeTagSize(fieldNumber) + computeFloatSizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code uint64} field, including tag. - */ - public static int computeUInt64Size(final int fieldNumber, final long value) { - return computeTagSize(fieldNumber) + computeUInt64SizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode an - * {@code int64} field, including tag. - */ - public static int computeInt64Size(final int fieldNumber, final long value) { - return computeTagSize(fieldNumber) + computeInt64SizeNoTag(value); - } + // ================================================================= /** * Compute the number of bytes that would be needed to encode an @@ -614,96 +469,6 @@ public final class CodedOutputStream { return computeTagSize(fieldNumber) + computeInt32SizeNoTag(value); } - /** - * Compute the number of bytes that would be needed to encode a - * {@code fixed64} field, including tag. - */ - public static int computeFixed64Size(final int fieldNumber, - final long value) { - return computeTagSize(fieldNumber) + computeFixed64SizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code fixed32} field, including tag. - */ - public static int computeFixed32Size(final int fieldNumber, - final int value) { - return computeTagSize(fieldNumber) + computeFixed32SizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code bool} field, including tag. - */ - public static int computeBoolSize(final int fieldNumber, - final boolean value) { - return computeTagSize(fieldNumber) + computeBoolSizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code string} field, including tag. - */ - public static int computeStringSize(final int fieldNumber, - final String value) { - return computeTagSize(fieldNumber) + computeStringSizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code group} field, including tag. - */ - public static int computeGroupSize(final int fieldNumber, - final MessageLite value) { - return computeTagSize(fieldNumber) * 2 + computeGroupSizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode an - * embedded message field, including tag. - */ - public static int computeMessageSize(final int fieldNumber, - final MessageLite value) { - return computeTagSize(fieldNumber) + computeMessageSizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code bytes} field, including tag. - */ - public static int computeBytesSize(final int fieldNumber, - final ByteString value) { - return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code bytes} field, including tag. - */ - public static int computeByteArraySize(final int fieldNumber, - final byte[] value) { - return computeTagSize(fieldNumber) + computeByteArraySizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code bytes} field, including tag. - */ - public static int computeByteBufferSize(final int fieldNumber, - final ByteBuffer value) { - return computeTagSize(fieldNumber) + computeByteBufferSizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode an - * embedded message in lazy field, including tag. - */ - public static int computeLazyFieldSize(final int fieldNumber, - final LazyFieldLite value) { - return computeTagSize(fieldNumber) + computeLazyFieldSizeNoTag(value); - } - /** * Compute the number of bytes that would be needed to encode a * {@code uint32} field, including tag. @@ -714,37 +479,42 @@ public final class CodedOutputStream { /** * Compute the number of bytes that would be needed to encode an - * enum field, including tag. Caller is responsible for converting the - * enum value to its numeric value. + * {@code sint32} field, including tag. */ - public static int computeEnumSize(final int fieldNumber, final int value) { - return computeTagSize(fieldNumber) + computeEnumSizeNoTag(value); + public static int computeSInt32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeSInt32SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed32} field, including tag. + */ + public static int computeFixed32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeFixed32SizeNoTag(value); } /** * Compute the number of bytes that would be needed to encode an * {@code sfixed32} field, including tag. */ - public static int computeSFixed32Size(final int fieldNumber, - final int value) { + public static int computeSFixed32Size(final int fieldNumber, final int value) { return computeTagSize(fieldNumber) + computeSFixed32SizeNoTag(value); } /** * Compute the number of bytes that would be needed to encode an - * {@code sfixed64} field, including tag. + * {@code int64} field, including tag. */ - public static int computeSFixed64Size(final int fieldNumber, - final long value) { - return computeTagSize(fieldNumber) + computeSFixed64SizeNoTag(value); + public static int computeInt64Size(final int fieldNumber, final long value) { + return computeTagSize(fieldNumber) + computeInt64SizeNoTag(value); } /** - * Compute the number of bytes that would be needed to encode an - * {@code sint32} field, including tag. + * Compute the number of bytes that would be needed to encode a + * {@code uint64} field, including tag. */ - public static int computeSInt32Size(final int fieldNumber, final int value) { - return computeTagSize(fieldNumber) + computeSInt32SizeNoTag(value); + public static int computeUInt64Size(final int fieldNumber, final long value) { + return computeTagSize(fieldNumber) + computeUInt64SizeNoTag(value); } /** @@ -755,16 +525,112 @@ public final class CodedOutputStream { return computeTagSize(fieldNumber) + computeSInt64SizeNoTag(value); } + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed64} field, including tag. + */ + public static int computeFixed64Size(final int fieldNumber, final long value) { + return computeTagSize(fieldNumber) + computeFixed64SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed64} field, including tag. + */ + public static int computeSFixed64Size(final int fieldNumber, final long value) { + return computeTagSize(fieldNumber) + computeSFixed64SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code float} field, including tag. + */ + public static int computeFloatSize(final int fieldNumber, final float value) { + return computeTagSize(fieldNumber) + computeFloatSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code double} field, including tag. + */ + public static int computeDoubleSize(final int fieldNumber, final double value) { + return computeTagSize(fieldNumber) + computeDoubleSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bool} field, including tag. + */ + public static int computeBoolSize(final int fieldNumber, final boolean value) { + return computeTagSize(fieldNumber) + computeBoolSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * enum field, including tag. The provided value is the numeric + * value used to represent the enum value on the wire (not the enum ordinal value). + */ + public static int computeEnumSize(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeEnumSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code string} field, including tag. + */ + public static int computeStringSize(final int fieldNumber, final String value) { + return computeTagSize(fieldNumber) + computeStringSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bytes} field, including tag. + */ + public static int computeBytesSize(final int fieldNumber, final ByteString value) { + return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bytes} field, including tag. + */ + public static int computeByteArraySize(final int fieldNumber, final byte[] value) { + return computeTagSize(fieldNumber) + computeByteArraySizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bytes} field, including tag. + */ + public static int computeByteBufferSize(final int fieldNumber, final ByteBuffer value) { + return computeTagSize(fieldNumber) + computeByteBufferSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * embedded message in lazy field, including tag. + */ + public static int computeLazyFieldSize(final int fieldNumber, final LazyFieldLite value) { + return computeTagSize(fieldNumber) + computeLazyFieldSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * embedded message field, including tag. + */ + public static int computeMessageSize(final int fieldNumber, final MessageLite value) { + return computeTagSize(fieldNumber) + computeMessageSizeNoTag(value); + } + /** * Compute the number of bytes that would be needed to encode a * MessageSet extension to the stream. For historical reasons, * the wire format differs from normal fields. */ - public static int computeMessageSetExtensionSize( - final int fieldNumber, final MessageLite value) { - return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + - computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + - computeMessageSize(WireFormat.MESSAGE_SET_MESSAGE, value); + public static int computeMessageSetExtensionSize(final int fieldNumber, final MessageLite value) { + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + + computeMessageSize(WireFormat.MESSAGE_SET_MESSAGE, value); } /** @@ -774,9 +640,9 @@ public final class CodedOutputStream { */ public static int computeRawMessageSetExtensionSize( final int fieldNumber, final ByteString value) { - return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + - computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + - computeBytesSize(WireFormat.MESSAGE_SET_MESSAGE, value); + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + + computeBytesSize(WireFormat.MESSAGE_SET_MESSAGE, value); } /** @@ -786,43 +652,16 @@ public final class CodedOutputStream { */ public static int computeLazyFieldMessageSetExtensionSize( final int fieldNumber, final LazyFieldLite value) { - return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + - computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + - computeLazyFieldSize(WireFormat.MESSAGE_SET_MESSAGE, value); + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + + computeLazyFieldSize(WireFormat.MESSAGE_SET_MESSAGE, value); } // ----------------------------------------------------------------- - /** - * Compute the number of bytes that would be needed to encode a - * {@code double} field, including tag. - */ - public static int computeDoubleSizeNoTag(final double value) { - return LITTLE_ENDIAN_64_SIZE; - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code float} field, including tag. - */ - public static int computeFloatSizeNoTag(final float value) { - return LITTLE_ENDIAN_32_SIZE; - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code uint64} field, including tag. - */ - public static int computeUInt64SizeNoTag(final long value) { - return computeRawVarint64Size(value); - } - - /** - * Compute the number of bytes that would be needed to encode an - * {@code int64} field, including tag. - */ - public static int computeInt64SizeNoTag(final long value) { - return computeRawVarint64Size(value); + /** Compute the number of bytes that would be needed to encode a tag. */ + public static int computeTagSize(final int fieldNumber) { + return computeUInt32SizeNoTag(WireFormat.makeTag(fieldNumber, 0)); } /** @@ -831,37 +670,148 @@ public final class CodedOutputStream { */ public static int computeInt32SizeNoTag(final int value) { if (value >= 0) { - return computeRawVarint32Size(value); + return computeUInt32SizeNoTag(value); } else { // Must sign-extend. - return 10; + return MAX_VARINT_SIZE; } } /** * Compute the number of bytes that would be needed to encode a - * {@code fixed64} field. + * {@code uint32} field. */ - public static int computeFixed64SizeNoTag(final long value) { - return LITTLE_ENDIAN_64_SIZE; + public static int computeUInt32SizeNoTag(final int value) { + if ((value & (~0 << 7)) == 0) { + return 1; + } + if ((value & (~0 << 14)) == 0) { + return 2; + } + if ((value & (~0 << 21)) == 0) { + return 3; + } + if ((value & (~0 << 28)) == 0) { + return 4; + } + return 5; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint32} field. + */ + public static int computeSInt32SizeNoTag(final int value) { + return computeUInt32SizeNoTag(encodeZigZag32(value)); } /** * Compute the number of bytes that would be needed to encode a * {@code fixed32} field. */ - public static int computeFixed32SizeNoTag(final int value) { - return LITTLE_ENDIAN_32_SIZE; + public static int computeFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) { + return FIXED_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed32} field. + */ + public static int computeSFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) { + return FIXED_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int64} field, including tag. + */ + public static int computeInt64SizeNoTag(final long value) { + return computeUInt64SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint64} field, including tag. + */ + public static int computeUInt64SizeNoTag(long value) { + // handle two popular special cases up front ... + if ((value & (~0L << 7)) == 0L) { + return 1; + } + if (value < 0L) { + return 10; + } + // ... leaving us with 8 remaining, which we can divide and conquer + int n = 2; + if ((value & (~0L << 35)) != 0L) { + n += 4; value >>>= 28; + } + if ((value & (~0L << 21)) != 0L) { + n += 2; value >>>= 14; + } + if ((value & (~0L << 14)) != 0L) { + n += 1; + } + return n; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint64} field. + */ + public static int computeSInt64SizeNoTag(final long value) { + return computeUInt64SizeNoTag(encodeZigZag64(value)); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed64} field. + */ + public static int computeFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) { + return FIXED_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed64} field. + */ + public static int computeSFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) { + return FIXED_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code float} field, including tag. + */ + public static int computeFloatSizeNoTag(@SuppressWarnings("unused") final float unused) { + return FIXED_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code double} field, including tag. + */ + public static int computeDoubleSizeNoTag(@SuppressWarnings("unused") final double unused) { + return FIXED_64_SIZE; } /** * Compute the number of bytes that would be needed to encode a * {@code bool} field. */ - public static int computeBoolSizeNoTag(final boolean value) { + public static int computeBoolSizeNoTag(@SuppressWarnings("unused") final boolean unused) { return 1; } + /** + * Compute the number of bytes that would be needed to encode an enum field. + * The provided value is the numeric value used to represent the enum value on the wire + * (not the enum ordinal value). + */ + public static int computeEnumSizeNoTag(final int value) { + return computeInt32SizeNoTag(value); + } + /** * Compute the number of bytes that would be needed to encode a * {@code string} field. @@ -876,24 +826,7 @@ public final class CodedOutputStream { length = bytes.length; } - return computeRawVarint32Size(length) + length; - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code group} field. - */ - public static int computeGroupSizeNoTag(final MessageLite value) { - return value.getSerializedSize(); - } - - /** - * Compute the number of bytes that would be needed to encode an embedded - * message field. - */ - public static int computeMessageSizeNoTag(final MessageLite value) { - final int size = value.getSerializedSize(); - return computeRawVarint32Size(size) + size; + return computeLengthDelimitedFieldSize(length); } /** @@ -901,8 +834,7 @@ public final class CodedOutputStream { * message stored in lazy field. */ public static int computeLazyFieldSizeNoTag(final LazyFieldLite value) { - final int size = value.getSerializedSize(); - return computeRawVarint32Size(size) + size; + return computeLengthDelimitedFieldSize(value.getSerializedSize()); } /** @@ -910,8 +842,7 @@ public final class CodedOutputStream { * {@code bytes} field. */ public static int computeBytesSizeNoTag(final ByteString value) { - return computeRawVarint32Size(value.size()) + - value.size(); + return computeLengthDelimitedFieldSize(value.size()); } /** @@ -919,7 +850,7 @@ public final class CodedOutputStream { * {@code bytes} field. */ public static int computeByteArraySizeNoTag(final byte[] value) { - return computeRawVarint32Size(value.length) + value.length; + return computeLengthDelimitedFieldSize(value.length); } /** @@ -927,379 +858,21 @@ public final class CodedOutputStream { * {@code bytes} field. */ public static int computeByteBufferSizeNoTag(final ByteBuffer value) { - return computeRawVarint32Size(value.capacity()) + value.capacity(); + return computeLengthDelimitedFieldSize(value.capacity()); } /** - * Compute the number of bytes that would be needed to encode a - * {@code uint32} field. + * Compute the number of bytes that would be needed to encode an embedded + * message field. */ - public static int computeUInt32SizeNoTag(final int value) { - return computeRawVarint32Size(value); + public static int computeMessageSizeNoTag(final MessageLite value) { + return computeLengthDelimitedFieldSize(value.getSerializedSize()); } - /** - * Compute the number of bytes that would be needed to encode an enum field. - * Caller is responsible for converting the enum value to its numeric value. - */ - public static int computeEnumSizeNoTag(final int value) { - return computeInt32SizeNoTag(value); + private static int computeLengthDelimitedFieldSize(int fieldLength) { + return computeUInt32SizeNoTag(fieldLength) + fieldLength; } - /** - * Compute the number of bytes that would be needed to encode an - * {@code sfixed32} field. - */ - public static int computeSFixed32SizeNoTag(final int value) { - return LITTLE_ENDIAN_32_SIZE; - } - - /** - * Compute the number of bytes that would be needed to encode an - * {@code sfixed64} field. - */ - public static int computeSFixed64SizeNoTag(final long value) { - return LITTLE_ENDIAN_64_SIZE; - } - - /** - * Compute the number of bytes that would be needed to encode an - * {@code sint32} field. - */ - public static int computeSInt32SizeNoTag(final int value) { - return computeRawVarint32Size(encodeZigZag32(value)); - } - - /** - * Compute the number of bytes that would be needed to encode an - * {@code sint64} field. - */ - public static int computeSInt64SizeNoTag(final long value) { - return computeRawVarint64Size(encodeZigZag64(value)); - } - - // ================================================================= - - /** - * Internal helper that writes the current buffer to the output. The - * buffer position is reset to its initial value when this returns. - */ - private void refreshBuffer() throws IOException { - if (output == null) { - // We're writing to a single buffer. - throw new OutOfSpaceException(); - } - - // Since we have an output stream, this is our buffer - // and buffer offset == 0 - output.write(buffer, 0, position); - position = 0; - } - - /** - * Flushes the stream and forces any buffered bytes to be written. This - * does not flush the underlying OutputStream. - */ - public void flush() throws IOException { - if (output != null) { - refreshBuffer(); - } - } - - /** - * If writing to a flat array, return the space left in the array. - * Otherwise, throws {@code UnsupportedOperationException}. - */ - public int spaceLeft() { - if (output == null) { - return limit - position; - } else { - throw new UnsupportedOperationException( - "spaceLeft() can only be called on CodedOutputStreams that are " + - "writing to a flat array."); - } - } - - /** - * Verifies that {@link #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 {@code CodedOutputStream}. Calling {@code checkNoSpaceLeft()} - * after writing verifies that the message was actually as big as expected, - * which can help catch bugs. - */ - public void checkNoSpaceLeft() { - if (spaceLeft() != 0) { - throw new IllegalStateException( - "Did not write as much data as expected."); - } - } - - /** - * If you create a CodedOutputStream around a simple flat array, you must - * not attempt to write more bytes than the array has space. Otherwise, - * this exception will be thrown. - */ - public static class OutOfSpaceException extends IOException { - private static final long serialVersionUID = -6947486886997889499L; - - private static final String MESSAGE = - "CodedOutputStream was writing to a flat byte array and ran out of space."; - - OutOfSpaceException() { - super(MESSAGE); - } - - OutOfSpaceException(Throwable cause) { - super(MESSAGE, cause); - } - } - - /** - * Get the total number of bytes successfully written to this stream. The - * returned value is not guaranteed to be accurate if exceptions have been - * found in the middle of writing. - */ - public int getTotalBytesWritten() { - return totalBytesWritten; - } - - /** Write a single byte. */ - public void writeRawByte(final byte value) throws IOException { - if (position == limit) { - refreshBuffer(); - } - - buffer[position++] = value; - ++totalBytesWritten; - } - - /** Write a single byte, represented by an integer value. */ - public void writeRawByte(final int value) throws IOException { - writeRawByte((byte) value); - } - - /** Write a byte string. */ - public void writeRawBytes(final ByteString value) throws IOException { - writeRawBytes(value, 0, value.size()); - } - - /** Write an array of bytes. */ - public void writeRawBytes(final byte[] value) throws IOException { - writeRawBytes(value, 0, value.length); - } - - /** - * Write a ByteBuffer. This method will write all content of the ByteBuffer - * regardless of the current position and limit (i.e., the number of bytes - * to be written is value.capacity(), not value.remaining()). Furthermore, - * this method doesn't alter the state of the passed-in ByteBuffer. Its - * position, limit, mark, etc. will remain unchanged. If you only want to - * write the remaining bytes of a ByteBuffer, you can call - * {@code writeRawBytes(byteBuffer.slice())}. - */ - public void writeRawBytes(final ByteBuffer value) throws IOException { - if (value.hasArray()) { - writeRawBytes(value.array(), value.arrayOffset(), value.capacity()); - } else { - ByteBuffer duplicated = value.duplicate(); - duplicated.clear(); - writeRawBytesInternal(duplicated); - } - } - - /** Write a ByteBuffer that isn't backed by an array. */ - private void writeRawBytesInternal(final ByteBuffer value) - throws IOException { - int length = value.remaining(); - if (limit - position >= length) { - // We have room in the current buffer. - value.get(buffer, position, length); - position += length; - totalBytesWritten += length; - } else { - // Write extends past current buffer. Fill the rest of this buffer and - // flush. - final int bytesWritten = limit - position; - value.get(buffer, position, bytesWritten); - length -= bytesWritten; - position = limit; - totalBytesWritten += bytesWritten; - refreshBuffer(); - - // Now deal with the rest. - // Since we have an output stream, this is our buffer - // and buffer offset == 0 - while (length > limit) { - // Copy data into the buffer before writing it to OutputStream. - // TODO(xiaofeng): Introduce ZeroCopyOutputStream to avoid this copy. - value.get(buffer, 0, limit); - output.write(buffer, 0, limit); - length -= limit; - totalBytesWritten += limit; - } - value.get(buffer, 0, length); - position = length; - totalBytesWritten += length; - } - } - - /** Write part of an array of bytes. */ - public void writeRawBytes(final byte[] value, int offset, int length) - throws IOException { - if (limit - position >= length) { - // We have room in the current buffer. - System.arraycopy(value, offset, buffer, position, length); - position += length; - totalBytesWritten += length; - } else { - // Write extends past current buffer. Fill the rest of this buffer and - // flush. - final int bytesWritten = limit - position; - System.arraycopy(value, offset, buffer, position, bytesWritten); - offset += bytesWritten; - length -= bytesWritten; - position = limit; - totalBytesWritten += bytesWritten; - refreshBuffer(); - - // Now deal with the rest. - // Since we have an output stream, this is our buffer - // and buffer offset == 0 - if (length <= limit) { - // Fits in new buffer. - System.arraycopy(value, offset, buffer, 0, length); - position = length; - } else { - // Write is very big. Let's do it all at once. - output.write(value, offset, length); - } - totalBytesWritten += length; - } - } - - /** Write part of a byte string. */ - public void writeRawBytes(final ByteString value, int offset, int length) - throws IOException { - if (limit - position >= length) { - // We have room in the current buffer. - value.copyTo(buffer, offset, position, length); - position += length; - totalBytesWritten += length; - } else { - // Write extends past current buffer. Fill the rest of this buffer and - // flush. - final int bytesWritten = limit - position; - value.copyTo(buffer, offset, position, bytesWritten); - offset += bytesWritten; - length -= bytesWritten; - position = limit; - totalBytesWritten += bytesWritten; - refreshBuffer(); - - // Now deal with the rest. - // Since we have an output stream, this is our buffer - // and buffer offset == 0 - if (length <= limit) { - // Fits in new buffer. - value.copyTo(buffer, offset, 0, length); - position = length; - } else { - value.writeTo(output, offset, length); - } - totalBytesWritten += length; - } - } - - /** Encode and write a tag. */ - public void writeTag(final int fieldNumber, final int wireType) - throws IOException { - writeRawVarint32(WireFormat.makeTag(fieldNumber, wireType)); - } - - /** Compute the number of bytes that would be needed to encode a tag. */ - public static int computeTagSize(final int fieldNumber) { - return computeRawVarint32Size(WireFormat.makeTag(fieldNumber, 0)); - } - - /** - * Encode and write a varint. {@code value} is treated as - * unsigned, so it won't be sign-extended if negative. - */ - public void writeRawVarint32(int value) throws IOException { - while (true) { - if ((value & ~0x7F) == 0) { - writeRawByte(value); - return; - } else { - writeRawByte((value & 0x7F) | 0x80); - value >>>= 7; - } - } - } - - /** - * Compute the number of bytes that would be needed to encode a varint. - * {@code value} is treated as unsigned, so it won't be sign-extended if - * negative. - */ - public static int computeRawVarint32Size(final int value) { - if ((value & (~0 << 7)) == 0) return 1; - if ((value & (~0 << 14)) == 0) return 2; - if ((value & (~0 << 21)) == 0) return 3; - if ((value & (~0 << 28)) == 0) return 4; - return 5; - } - - /** Encode and write a varint. */ - public void writeRawVarint64(long value) throws IOException { - while (true) { - if ((value & ~0x7FL) == 0) { - writeRawByte((int)value); - return; - } else { - writeRawByte(((int)value & 0x7F) | 0x80); - value >>>= 7; - } - } - } - - /** Compute the number of bytes that would be needed to encode a varint. */ - public static int computeRawVarint64Size(long value) { - // handle two popular special cases up front ... - if ((value & (~0L << 7)) == 0L) return 1; - if (value < 0L) return 10; - // ... leaving us with 8 remaining, which we can divide and conquer - int n = 2; - if ((value & (~0L << 35)) != 0L) { n += 4; value >>>= 28; } - if ((value & (~0L << 21)) != 0L) { n += 2; value >>>= 14; } - if ((value & (~0L << 14)) != 0L) { n += 1; } - return n; - } - - /** Write a little-endian 32-bit integer. */ - public void writeRawLittleEndian32(final int value) throws IOException { - writeRawByte((value ) & 0xFF); - writeRawByte((value >> 8) & 0xFF); - writeRawByte((value >> 16) & 0xFF); - writeRawByte((value >> 24) & 0xFF); - } - - public static final int LITTLE_ENDIAN_32_SIZE = 4; - - /** Write a little-endian 64-bit integer. */ - public void writeRawLittleEndian64(final long value) throws IOException { - writeRawByte((int)(value ) & 0xFF); - writeRawByte((int)(value >> 8) & 0xFF); - writeRawByte((int)(value >> 16) & 0xFF); - writeRawByte((int)(value >> 24) & 0xFF); - writeRawByte((int)(value >> 32) & 0xFF); - writeRawByte((int)(value >> 40) & 0xFF); - writeRawByte((int)(value >> 48) & 0xFF); - writeRawByte((int)(value >> 56) & 0xFF); - } - - public static final int LITTLE_ENDIAN_64_SIZE = 8; - /** * Encode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers * into values that can be efficiently encoded with varint. (Otherwise, @@ -1329,4 +902,1763 @@ public final class CodedOutputStream { // Note: the right-shift must be arithmetic return (n << 1) ^ (n >> 63); } + + // ================================================================= + + /** + * Flushes the stream and forces any buffered bytes to be written. This + * does not flush the underlying OutputStream. + */ + public abstract void flush() throws IOException; + + /** + * If writing to a flat array, return the space left in the array. + * Otherwise, throws {@code UnsupportedOperationException}. + */ + public abstract int spaceLeft(); + + /** + * Verifies that {@link #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 {@code CodedOutputStream}. Calling {@code checkNoSpaceLeft()} + * after writing verifies that the message was actually as big as expected, + * which can help catch bugs. + */ + public final void checkNoSpaceLeft() { + if (spaceLeft() != 0) { + throw new IllegalStateException("Did not write as much data as expected."); + } + } + + /** + * If you create a CodedOutputStream around a simple flat array, you must + * not attempt to write more bytes than the array has space. Otherwise, + * this exception will be thrown. + */ + public static class OutOfSpaceException extends IOException { + private static final long serialVersionUID = -6947486886997889499L; + + private static final String MESSAGE = + "CodedOutputStream was writing to a flat byte array and ran out of space."; + + OutOfSpaceException() { + super(MESSAGE); + } + + OutOfSpaceException(Throwable cause) { + super(MESSAGE, cause); + } + } + + /** + * Get the total number of bytes successfully written to this stream. The + * returned value is not guaranteed to be accurate if exceptions have been + * found in the middle of writing. + */ + public abstract int getTotalBytesWritten(); + + // ================================================================= + + /** Write a {@code bytes} field to the stream. Visible for testing. */ + abstract void writeByteArrayNoTag(final byte[] value, final int offset, final int length) + throws IOException; + + final void inefficientWriteStringNoTag(String value, UnpairedSurrogateException cause) + throws IOException { + logger.log(Level.WARNING, + "Converting ill-formed UTF-16. Your Protocol Buffer will not round trip correctly!", cause); + + // Unfortunately there does not appear to be any way to tell Java to encode + // UTF-8 directly into our buffer, so we have to let it create its own byte + // array and then copy. + // TODO(dweis): Consider using nio Charset methods instead. + final byte[] bytes = value.getBytes(Internal.UTF_8); + try { + writeUInt32NoTag(bytes.length); + writeLazy(bytes, 0, bytes.length); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException(e); + } catch (OutOfSpaceException e) { + throw e; + } + } + + // ================================================================= + + /** + * Write a {@code group} field, including tag, to the stream. + * + * @deprecated groups are deprecated. + */ + @Deprecated + public final void writeGroup(final int fieldNumber, final MessageLite value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); + writeGroupNoTag(value); + writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); + } + + /** + * Write a {@code group} field to the stream. + * + * @deprecated groups are deprecated. + */ + @Deprecated + public final void writeGroupNoTag(final MessageLite value) throws IOException { + value.writeTo(this); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code group} field, including tag. + * + * @deprecated groups are deprecated. + */ + @Deprecated + public static int computeGroupSize(final int fieldNumber, final MessageLite value) { + return computeTagSize(fieldNumber) * 2 + computeGroupSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code group} field. + */ + @Deprecated + public static int computeGroupSizeNoTag(final MessageLite value) { + return value.getSerializedSize(); + } + + /** + * Encode and write a varint. {@code value} is treated as + * unsigned, so it won't be sign-extended if negative. + * + * @deprecated use {@link #writeUInt32NoTag} instead. + */ + @Deprecated + public final void writeRawVarint32(int value) throws IOException { + writeUInt32NoTag(value); + } + + /** + * Encode and write a varint. + * + * @deprecated use {@link #writeUInt64NoTag} instead. + */ + @Deprecated + public final void writeRawVarint64(long value) throws IOException { + writeUInt64NoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a varint. + * {@code value} is treated as unsigned, so it won't be sign-extended if + * negative. + * + * @deprecated use {@link #computeUInt32SizeNoTag(int)} instead. + */ + @Deprecated + public static int computeRawVarint32Size(final int value) { + return computeUInt32SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a varint. + * + * @deprecated use {@link #computeUInt64SizeNoTag(long)} instead. + */ + @Deprecated + public static int computeRawVarint64Size(long value) { + return computeUInt64SizeNoTag(value); + } + + /** + * Write a little-endian 32-bit integer. + * + * @deprecated Use {@link #writeFixed32NoTag} instead. + */ + @Deprecated + public final void writeRawLittleEndian32(final int value) throws IOException { + writeFixed32NoTag(value); + } + + /** + * Write a little-endian 64-bit integer. + * + * @deprecated Use {@link #writeFixed64NoTag} instead. + */ + @Deprecated + public final void writeRawLittleEndian64(final long value) throws IOException { + writeFixed64NoTag(value); + } + + // ================================================================= + + /** + * A {@link CodedOutputStream} that writes directly to a byte array. + */ + private static class ArrayEncoder extends CodedOutputStream { + private final byte[] buffer; + private final int offset; + private final int limit; + private int position; + + ArrayEncoder(byte[] buffer, int offset, int length) { + if (buffer == null) { + throw new NullPointerException("buffer"); + } + if ((offset | length | (buffer.length - (offset + length))) < 0) { + throw new IllegalArgumentException(String.format( + "Array range is invalid. Buffer.length=%d, offset=%d, length=%d", + buffer.length, offset, length)); + } + this.buffer = buffer; + this.offset = offset; + position = offset; + limit = offset + length; + } + + @Override + public final void writeTag(final int fieldNumber, final int wireType) throws IOException { + writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType)); + } + + @Override + public final void writeInt32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeInt32NoTag(value); + } + + @Override + public final void writeUInt32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeUInt32NoTag(value); + } + + @Override + public final void writeFixed32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeFixed32NoTag(value); + } + + @Override + public final void writeUInt64(final int fieldNumber, final long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeUInt64NoTag(value); + } + + @Override + public final void writeFixed64(final int fieldNumber, final long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeFixed64NoTag(value); + } + + @Override + public final void writeBool(final int fieldNumber, final boolean value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + write((byte) (value ? 1 : 0)); + } + + @Override + public final void writeString(final int fieldNumber, final String value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeStringNoTag(value); + } + + @Override + public final void writeBytes(final int fieldNumber, final ByteString value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeBytesNoTag(value); + } + + @Override + public final void writeByteArray(final int fieldNumber, final byte[] value) throws IOException { + writeByteArray(fieldNumber, value, 0, value.length); + } + + @Override + public final void writeByteArray( + final int fieldNumber, final byte[] value, final int offset, final int length) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeByteArrayNoTag(value, offset, length); + } + + @Override + public final void writeByteBuffer(final int fieldNumber, final ByteBuffer value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeUInt32NoTag(value.capacity()); + writeRawBytes(value); + } + + @Override + public final void writeBytesNoTag(final ByteString value) throws IOException { + writeUInt32NoTag(value.size()); + value.writeTo(this); + } + + @Override + public final void writeByteArrayNoTag(final byte[] value, int offset, int length) + throws IOException { + writeUInt32NoTag(length); + write(value, offset, length); + } + + @Override + public final void writeRawBytes(final ByteBuffer value) throws IOException { + if (value.hasArray()) { + write(value.array(), value.arrayOffset(), value.capacity()); + } else { + ByteBuffer duplicated = value.duplicate(); + duplicated.clear(); + write(duplicated); + } + } + + @Override + public final void writeMessage(final int fieldNumber, final MessageLite value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeMessageNoTag(value); + } + + @Override + public final void writeMessageSetExtension(final int fieldNumber, final MessageLite value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public final void writeRawMessageSetExtension(final int fieldNumber, final ByteString value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public final void writeMessageNoTag(final MessageLite value) throws IOException { + writeUInt32NoTag(value.getSerializedSize()); + value.writeTo(this); + } + + @Override + public final void write(byte value) throws IOException { + try { + buffer[position++] = value; + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException(new IndexOutOfBoundsException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1))); + } + } + + @Override + public final void writeInt32NoTag(int value) throws IOException { + if (value >= 0) { + writeUInt32NoTag(value); + } else { + // Must sign-extend. + writeUInt64NoTag(value); + } + } + + @Override + public final void writeUInt32NoTag(int value) throws IOException { + if (HAS_UNSAFE_ARRAY_OPERATIONS && spaceLeft() >= MAX_VARINT_SIZE) { + long pos = ARRAY_BASE_OFFSET + position; + while (true) { + if ((value & ~0x7F) == 0) { + UNSAFE.putByte(buffer, pos++, (byte) value); + position++; + return; + } else { + UNSAFE.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80)); + position++; + value >>>= 7; + } + } + } else { + try { + while (true) { + if ((value & ~0x7F) == 0) { + buffer[position++] = (byte) value; + return; + } else { + buffer[position++] = (byte) ((value & 0x7F) | 0x80); + value >>>= 7; + } + } + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException( + new IndexOutOfBoundsException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1))); + } + } + } + + @Override + public final void writeFixed32NoTag(int value) throws IOException { + try { + buffer[position++] = (byte) (value & 0xFF); + buffer[position++] = (byte) ((value >> 8) & 0xFF); + buffer[position++] = (byte) ((value >> 16) & 0xFF); + buffer[position++] = (byte) ((value >> 24) & 0xFF); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException( + new IndexOutOfBoundsException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1))); + } + } + + @Override + public final void writeUInt64NoTag(long value) throws IOException { + if (HAS_UNSAFE_ARRAY_OPERATIONS && spaceLeft() >= MAX_VARINT_SIZE) { + long pos = ARRAY_BASE_OFFSET + position; + while (true) { + if ((value & ~0x7FL) == 0) { + UNSAFE.putByte(buffer, pos++, (byte) value); + position++; + return; + } else { + UNSAFE.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80)); + position++; + value >>>= 7; + } + } + } else { + try { + while (true) { + if ((value & ~0x7FL) == 0) { + buffer[position++] = (byte) value; + return; + } else { + buffer[position++] = (byte) (((int) value & 0x7F) | 0x80); + value >>>= 7; + } + } + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException( + new IndexOutOfBoundsException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1))); + } + } + } + + @Override + public final void writeFixed64NoTag(long value) throws IOException { + try { + buffer[position++] = (byte) ((int) (value) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 8) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 16) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 24) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 32) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 40) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 48) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 56) & 0xFF); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException( + new IndexOutOfBoundsException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1))); + } + } + + @Override + public final void write(byte[] value, int offset, int length) throws IOException { + try { + System.arraycopy(value, offset, buffer, position, length); + position += length; + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException( + new IndexOutOfBoundsException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, length))); + } + } + + @Override + public final void writeLazy(byte[] value, int offset, int length) throws IOException { + write(value, offset, length); + } + + @Override + public final void write(ByteBuffer value) throws IOException { + final int length = value.remaining(); + try { + value.get(buffer, position, length); + position += length; + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException( + new IndexOutOfBoundsException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, length))); + } + } + + @Override + public final void writeLazy(ByteBuffer value) throws IOException { + write(value); + } + + @Override + public final void writeStringNoTag(String value) throws IOException { + final int oldPosition = position; + try { + // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()), + // and at most 3 times of it. We take advantage of this in both branches below. + final int maxLength = value.length() * Utf8.MAX_BYTES_PER_CHAR; + final int maxLengthVarIntSize = computeUInt32SizeNoTag(maxLength); + final int minLengthVarIntSize = computeUInt32SizeNoTag(value.length()); + if (minLengthVarIntSize == maxLengthVarIntSize) { + position = oldPosition + minLengthVarIntSize; + int newPosition = Utf8.encode(value, buffer, position, spaceLeft()); + // Since this class is stateful and tracks the position, we rewind and store the state, + // prepend the length, then reset it back to the end of the string. + position = oldPosition; + int length = newPosition - oldPosition - minLengthVarIntSize; + writeUInt32NoTag(length); + position = newPosition; + } else { + int length = Utf8.encodedLength(value); + writeUInt32NoTag(length); + position = Utf8.encode(value, buffer, position, spaceLeft()); + } + } catch (UnpairedSurrogateException e) { + // Roll back the change - we fall back to inefficient path. + position = oldPosition; + + // TODO(nathanmittler): We should throw an IOException here instead. + inefficientWriteStringNoTag(value, e); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void flush() { + // Do nothing. + } + + @Override + public final int spaceLeft() { + return limit - position; + } + + @Override + public final int getTotalBytesWritten() { + return position - offset; + } + } + + /** + * A {@link CodedOutputStream} that writes directly to a heap {@link ByteBuffer}. Writes are + * done directly to the underlying array. The buffer position is only updated after a flush. + */ + private static final class NioHeapEncoder extends ArrayEncoder { + private final ByteBuffer byteBuffer; + private int initialPosition; + + NioHeapEncoder(ByteBuffer byteBuffer) { + super(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), + byteBuffer.remaining()); + this.byteBuffer = byteBuffer; + this.initialPosition = byteBuffer.position(); + } + + @Override + public void flush() { + // Update the position on the buffer. + byteBuffer.position(initialPosition + getTotalBytesWritten()); + } + } + + /** + * A {@link CodedOutputStream} that writes directly to a {@link ByteBuffer}. + */ + private static final class NioEncoder extends CodedOutputStream { + private final ByteBuffer originalBuffer; + private final ByteBuffer buffer; + private final int initialPosition; + + NioEncoder(ByteBuffer buffer) { + this.originalBuffer = buffer; + this.buffer = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN); + initialPosition = buffer.position(); + } + + @Override + public void writeTag(final int fieldNumber, final int wireType) throws IOException { + writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType)); + } + + @Override + public void writeInt32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeInt32NoTag(value); + } + + @Override + public void writeUInt32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeUInt32NoTag(value); + } + + @Override + public void writeFixed32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeFixed32NoTag(value); + } + + @Override + public void writeUInt64(final int fieldNumber, final long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeUInt64NoTag(value); + } + + @Override + public void writeFixed64(final int fieldNumber, final long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeFixed64NoTag(value); + } + + @Override + public void writeBool(final int fieldNumber, final boolean value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + write((byte) (value ? 1 : 0)); + } + + @Override + public void writeString(final int fieldNumber, final String value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeStringNoTag(value); + } + + @Override + public void writeBytes(final int fieldNumber, final ByteString value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeBytesNoTag(value); + } + + @Override + public void writeByteArray(final int fieldNumber, final byte[] value) throws IOException { + writeByteArray(fieldNumber, value, 0, value.length); + } + + @Override + public void writeByteArray( + final int fieldNumber, final byte[] value, final int offset, final int length) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeByteArrayNoTag(value, offset, length); + } + + @Override + public void writeByteBuffer(final int fieldNumber, final ByteBuffer value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeUInt32NoTag(value.capacity()); + writeRawBytes(value); + } + + @Override + public void writeMessage(final int fieldNumber, final MessageLite value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeMessageNoTag(value); + } + + @Override + public void writeMessageSetExtension(final int fieldNumber, final MessageLite value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public void writeRawMessageSetExtension(final int fieldNumber, final ByteString value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public void writeMessageNoTag(final MessageLite value) throws IOException { + writeUInt32NoTag(value.getSerializedSize()); + value.writeTo(this); + } + + @Override + public void write(byte value) throws IOException { + try { + buffer.put(value); + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void writeBytesNoTag(final ByteString value) throws IOException { + writeUInt32NoTag(value.size()); + value.writeTo(this); + } + + @Override + public void writeByteArrayNoTag(final byte[] value, int offset, int length) throws IOException { + writeUInt32NoTag(length); + write(value, offset, length); + } + + @Override + public void writeRawBytes(final ByteBuffer value) throws IOException { + if (value.hasArray()) { + write(value.array(), value.arrayOffset(), value.capacity()); + } else { + ByteBuffer duplicated = value.duplicate(); + duplicated.clear(); + write(duplicated); + } + } + + @Override + public void writeInt32NoTag(int value) throws IOException { + if (value >= 0) { + writeUInt32NoTag(value); + } else { + // Must sign-extend. + writeUInt64NoTag(value); + } + } + + @Override + public void writeUInt32NoTag(int value) throws IOException { + try { + while (true) { + if ((value & ~0x7F) == 0) { + buffer.put((byte) value); + return; + } else { + buffer.put((byte) ((value & 0x7F) | 0x80)); + value >>>= 7; + } + } + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void writeFixed32NoTag(int value) throws IOException { + try { + buffer.putInt(value); + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void writeUInt64NoTag(long value) throws IOException { + try { + while (true) { + if ((value & ~0x7FL) == 0) { + buffer.put((byte) value); + return; + } else { + buffer.put((byte) (((int) value & 0x7F) | 0x80)); + value >>>= 7; + } + } + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void writeFixed64NoTag(long value) throws IOException { + try { + buffer.putLong(value); + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void write(byte[] value, int offset, int length) throws IOException { + try { + buffer.put(value, offset, length); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException(e); + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void writeLazy(byte[] value, int offset, int length) throws IOException { + write(value, offset, length); + } + + @Override + public void write(ByteBuffer value) throws IOException { + try { + buffer.put(value); + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void writeLazy(ByteBuffer value) throws IOException { + write(value); + } + + @Override + public void writeStringNoTag(String value) throws IOException { + final int startPos = buffer.position(); + try { + // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()), + // and at most 3 times of it. We take advantage of this in both branches below. + final int maxEncodedSize = value.length() * Utf8.MAX_BYTES_PER_CHAR; + final int maxLengthVarIntSize = computeUInt32SizeNoTag(maxEncodedSize); + final int minLengthVarIntSize = computeUInt32SizeNoTag(value.length()); + if (minLengthVarIntSize == maxLengthVarIntSize) { + // Save the current position and increment past the length field. We'll come back + // and write the length field after the encoding is complete. + final int startOfBytes = buffer.position() + minLengthVarIntSize; + buffer.position(startOfBytes); + + // Encode the string. + encode(value); + + // Now go back to the beginning and write the length. + int endOfBytes = buffer.position(); + buffer.position(startPos); + writeUInt32NoTag(endOfBytes - startOfBytes); + + // Reposition the buffer past the written data. + buffer.position(endOfBytes); + } else { + final int length = Utf8.encodedLength(value); + writeUInt32NoTag(length); + encode(value); + } + } catch (UnpairedSurrogateException e) { + // Roll back the change and convert to an IOException. + buffer.position(startPos); + + // TODO(nathanmittler): We should throw an IOException here instead. + inefficientWriteStringNoTag(value, e); + } catch (IllegalArgumentException e) { + // Thrown by buffer.position() if out of range. + throw new OutOfSpaceException(e); + } + } + + @Override + public void flush() { + // Update the position of the original buffer. + originalBuffer.position(buffer.position()); + } + + @Override + public int spaceLeft() { + return buffer.remaining(); + } + + @Override + public int getTotalBytesWritten() { + return buffer.position() - initialPosition; + } + + private void encode(String value) throws IOException { + try { + Utf8.encodeUtf8(value, buffer); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException(e); + } + } + } + + /** + * Abstract base class for buffered encoders. + */ + private abstract static class AbstractBufferedEncoder extends CodedOutputStream { + final byte[] buffer; + final int limit; + int position; + int totalBytesWritten; + + AbstractBufferedEncoder(int bufferSize) { + if (bufferSize < 0) { + throw new IllegalArgumentException("bufferSize must be >= 0"); + } + // As an optimization, we require that the buffer be able to store at least 2 + // varints so that we can buffer any integer write (tag + value). This reduces the + // number of range checks for a single write to 1 (i.e. if there is not enough space + // to buffer the tag+value, flush and then buffer it). + this.buffer = new byte[max(bufferSize, MAX_VARINT_SIZE * 2)]; + this.limit = buffer.length; + } + + @Override + public final int spaceLeft() { + throw new UnsupportedOperationException( + "spaceLeft() can only be called on CodedOutputStreams that are " + + "writing to a flat array or ByteBuffer."); + } + + @Override + public final int getTotalBytesWritten() { + return totalBytesWritten; + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void buffer(byte value) { + buffer[position++] = value; + totalBytesWritten++; + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void bufferTag(final int fieldNumber, final int wireType) { + bufferUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType)); + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void bufferInt32NoTag(final int value) { + if (value >= 0) { + bufferUInt32NoTag(value); + } else { + // Must sign-extend. + bufferUInt64NoTag(value); + } + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void bufferUInt32NoTag(int value) { + if (HAS_UNSAFE_ARRAY_OPERATIONS) { + final long originalPos = ARRAY_BASE_OFFSET + position; + long pos = originalPos; + while (true) { + if ((value & ~0x7F) == 0) { + UNSAFE.putByte(buffer, pos++, (byte) value); + break; + } else { + UNSAFE.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80)); + value >>>= 7; + } + } + int delta = (int) (pos - originalPos); + position += delta; + totalBytesWritten += delta; + } else { + while (true) { + if ((value & ~0x7F) == 0) { + buffer[position++] = (byte) value; + totalBytesWritten++; + return; + } else { + buffer[position++] = (byte) ((value & 0x7F) | 0x80); + totalBytesWritten++; + value >>>= 7; + } + } + } + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void bufferUInt64NoTag(long value) { + if (HAS_UNSAFE_ARRAY_OPERATIONS) { + final long originalPos = ARRAY_BASE_OFFSET + position; + long pos = originalPos; + while (true) { + if ((value & ~0x7FL) == 0) { + UNSAFE.putByte(buffer, pos++, (byte) value); + break; + } else { + UNSAFE.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80)); + value >>>= 7; + } + } + int delta = (int) (pos - originalPos); + position += delta; + totalBytesWritten += delta; + } else { + while (true) { + if ((value & ~0x7FL) == 0) { + buffer[position++] = (byte) value; + totalBytesWritten++; + return; + } else { + buffer[position++] = (byte) (((int) value & 0x7F) | 0x80); + totalBytesWritten++; + value >>>= 7; + } + } + } + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void bufferFixed32NoTag(int value) { + buffer[position++] = (byte) (value & 0xFF); + buffer[position++] = (byte) ((value >> 8) & 0xFF); + buffer[position++] = (byte) ((value >> 16) & 0xFF); + buffer[position++] = (byte) ((value >> 24) & 0xFF); + totalBytesWritten += FIXED_32_SIZE; + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void bufferFixed64NoTag(long value) { + buffer[position++] = (byte) (value & 0xFF); + buffer[position++] = (byte) ((value >> 8) & 0xFF); + buffer[position++] = (byte) ((value >> 16) & 0xFF); + buffer[position++] = (byte) ((value >> 24) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 32) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 40) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 48) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 56) & 0xFF); + totalBytesWritten += FIXED_64_SIZE; + } + } + + /** + * A {@link CodedOutputStream} that decorates a {@link ByteOutput}. It internal buffer only to + * support string encoding operations. All other writes are just passed through to the + * {@link ByteOutput}. + */ + private static final class ByteOutputEncoder extends AbstractBufferedEncoder { + private final ByteOutput out; + + ByteOutputEncoder(ByteOutput out, int bufferSize) { + super(bufferSize); + if (out == null) { + throw new NullPointerException("out"); + } + this.out = out; + } + + @Override + public void writeTag(final int fieldNumber, final int wireType) throws IOException { + writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType)); + } + + @Override + public void writeInt32(final int fieldNumber, final int value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE * 2); + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + bufferInt32NoTag(value); + } + + @Override + public void writeUInt32(final int fieldNumber, final int value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE * 2); + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + bufferUInt32NoTag(value); + } + + @Override + public void writeFixed32(final int fieldNumber, final int value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE + FIXED_32_SIZE); + bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + bufferFixed32NoTag(value); + } + + @Override + public void writeUInt64(final int fieldNumber, final long value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE * 2); + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + bufferUInt64NoTag(value); + } + + @Override + public void writeFixed64(final int fieldNumber, final long value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE + FIXED_64_SIZE); + bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + bufferFixed64NoTag(value); + } + + @Override + public void writeBool(final int fieldNumber, final boolean value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE + 1); + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + buffer((byte) (value ? 1 : 0)); + } + + @Override + public void writeString(final int fieldNumber, final String value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeStringNoTag(value); + } + + @Override + public void writeBytes(final int fieldNumber, final ByteString value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeBytesNoTag(value); + } + + @Override + public void writeByteArray(final int fieldNumber, final byte[] value) throws IOException { + writeByteArray(fieldNumber, value, 0, value.length); + } + + @Override + public void writeByteArray( + final int fieldNumber, final byte[] value, final int offset, final int length) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeByteArrayNoTag(value, offset, length); + } + + @Override + public void writeByteBuffer(final int fieldNumber, final ByteBuffer value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeUInt32NoTag(value.capacity()); + writeRawBytes(value); + } + + @Override + public void writeBytesNoTag(final ByteString value) throws IOException { + writeUInt32NoTag(value.size()); + value.writeTo(this); + } + + @Override + public void writeByteArrayNoTag(final byte[] value, int offset, int length) throws IOException { + writeUInt32NoTag(length); + write(value, offset, length); + } + + @Override + public void writeRawBytes(final ByteBuffer value) throws IOException { + if (value.hasArray()) { + write(value.array(), value.arrayOffset(), value.capacity()); + } else { + ByteBuffer duplicated = value.duplicate(); + duplicated.clear(); + write(duplicated); + } + } + + @Override + public void writeMessage(final int fieldNumber, final MessageLite value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeMessageNoTag(value); + } + + @Override + public void writeMessageSetExtension(final int fieldNumber, final MessageLite value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public void writeRawMessageSetExtension(final int fieldNumber, final ByteString value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public void writeMessageNoTag(final MessageLite value) throws IOException { + writeUInt32NoTag(value.getSerializedSize()); + value.writeTo(this); + } + + @Override + public void write(byte value) throws IOException { + if (position == limit) { + doFlush(); + } + + buffer(value); + } + + @Override + public void writeInt32NoTag(int value) throws IOException { + if (value >= 0) { + writeUInt32NoTag(value); + } else { + // Must sign-extend. + writeUInt64NoTag(value); + } + } + + @Override + public void writeUInt32NoTag(int value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE); + bufferUInt32NoTag(value); + } + + @Override + public void writeFixed32NoTag(final int value) throws IOException { + flushIfNotAvailable(FIXED_32_SIZE); + bufferFixed32NoTag(value); + } + + @Override + public void writeUInt64NoTag(long value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE); + bufferUInt64NoTag(value); + } + + @Override + public void writeFixed64NoTag(final long value) throws IOException { + flushIfNotAvailable(FIXED_64_SIZE); + bufferFixed64NoTag(value); + } + + @Override + public void writeStringNoTag(String value) throws IOException { + // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()), + // and at most 3 times of it. We take advantage of this in both branches below. + final int maxLength = value.length() * Utf8.MAX_BYTES_PER_CHAR; + final int maxLengthVarIntSize = computeUInt32SizeNoTag(maxLength); + + // If we are streaming and the potential length is too big to fit in our buffer, we take the + // slower path. + if (maxLengthVarIntSize + maxLength > limit) { + // Allocate a byte[] that we know can fit the string and encode into it. String.getBytes() + // does the same internally and then does *another copy* to return a byte[] of exactly the + // right size. We can skip that copy and just writeRawBytes up to the actualLength of the + // UTF-8 encoded bytes. + final byte[] encodedBytes = new byte[maxLength]; + int actualLength = Utf8.encode(value, encodedBytes, 0, maxLength); + writeUInt32NoTag(actualLength); + writeLazy(encodedBytes, 0, actualLength); + return; + } + + // Fast path: we have enough space available in our buffer for the string... + if (maxLengthVarIntSize + maxLength > limit - position) { + // Flush to free up space. + doFlush(); + } + + final int oldPosition = position; + try { + // Optimize for the case where we know this length results in a constant varint length as + // this saves a pass for measuring the length of the string. + final int minLengthVarIntSize = computeUInt32SizeNoTag(value.length()); + + if (minLengthVarIntSize == maxLengthVarIntSize) { + position = oldPosition + minLengthVarIntSize; + int newPosition = Utf8.encode(value, buffer, position, limit - position); + // Since this class is stateful and tracks the position, we rewind and store the state, + // prepend the length, then reset it back to the end of the string. + position = oldPosition; + int length = newPosition - oldPosition - minLengthVarIntSize; + bufferUInt32NoTag(length); + position = newPosition; + totalBytesWritten += length; + } else { + int length = Utf8.encodedLength(value); + bufferUInt32NoTag(length); + position = Utf8.encode(value, buffer, position, length); + totalBytesWritten += length; + } + } catch (UnpairedSurrogateException e) { + // Roll back the change and convert to an IOException. + totalBytesWritten -= position - oldPosition; + position = oldPosition; + + // TODO(nathanmittler): We should throw an IOException here instead. + inefficientWriteStringNoTag(value, e); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void flush() throws IOException { + if (position > 0) { + // Flush the buffer. + doFlush(); + } + } + + @Override + public void write(byte[] value, int offset, int length) throws IOException { + flush(); + out.write(value, offset, length); + totalBytesWritten += length; + } + + @Override + public void writeLazy(byte[] value, int offset, int length) throws IOException { + flush(); + out.writeLazy(value, offset, length); + totalBytesWritten += length; + } + + @Override + public void write(ByteBuffer value) throws IOException { + flush(); + int length = value.remaining(); + out.write(value); + totalBytesWritten += length; + } + + @Override + public void writeLazy(ByteBuffer value) throws IOException { + flush(); + int length = value.remaining(); + out.writeLazy(value); + totalBytesWritten += length; + } + + private void flushIfNotAvailable(int requiredSize) throws IOException { + if (limit - position < requiredSize) { + doFlush(); + } + } + + private void doFlush() throws IOException { + out.write(buffer, 0, position); + position = 0; + } + } + + /** + * An {@link CodedOutputStream} that decorates an {@link OutputStream}. It performs internal + * buffering to optimize writes to the {@link OutputStream}. + */ + private static final class OutputStreamEncoder extends AbstractBufferedEncoder { + private final OutputStream out; + + OutputStreamEncoder(OutputStream out, int bufferSize) { + super(bufferSize); + if (out == null) { + throw new NullPointerException("out"); + } + this.out = out; + } + + @Override + public void writeTag(final int fieldNumber, final int wireType) throws IOException { + writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType)); + } + + @Override + public void writeInt32(final int fieldNumber, final int value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE * 2); + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + bufferInt32NoTag(value); + } + + @Override + public void writeUInt32(final int fieldNumber, final int value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE * 2); + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + bufferUInt32NoTag(value); + } + + @Override + public void writeFixed32(final int fieldNumber, final int value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE + FIXED_32_SIZE); + bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + bufferFixed32NoTag(value); + } + + @Override + public void writeUInt64(final int fieldNumber, final long value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE * 2); + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + bufferUInt64NoTag(value); + } + + @Override + public void writeFixed64(final int fieldNumber, final long value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE + FIXED_64_SIZE); + bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + bufferFixed64NoTag(value); + } + + @Override + public void writeBool(final int fieldNumber, final boolean value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE + 1); + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + buffer((byte) (value ? 1 : 0)); + } + + @Override + public void writeString(final int fieldNumber, final String value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeStringNoTag(value); + } + + @Override + public void writeBytes(final int fieldNumber, final ByteString value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeBytesNoTag(value); + } + + @Override + public void writeByteArray(final int fieldNumber, final byte[] value) throws IOException { + writeByteArray(fieldNumber, value, 0, value.length); + } + + @Override + public void writeByteArray( + final int fieldNumber, final byte[] value, final int offset, final int length) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeByteArrayNoTag(value, offset, length); + } + + @Override + public void writeByteBuffer(final int fieldNumber, final ByteBuffer value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeUInt32NoTag(value.capacity()); + writeRawBytes(value); + } + + @Override + public void writeBytesNoTag(final ByteString value) throws IOException { + writeUInt32NoTag(value.size()); + value.writeTo(this); + } + + @Override + public void writeByteArrayNoTag(final byte[] value, int offset, int length) throws IOException { + writeUInt32NoTag(length); + write(value, offset, length); + } + + @Override + public void writeRawBytes(final ByteBuffer value) throws IOException { + if (value.hasArray()) { + write(value.array(), value.arrayOffset(), value.capacity()); + } else { + ByteBuffer duplicated = value.duplicate(); + duplicated.clear(); + write(duplicated); + } + } + + @Override + public void writeMessage(final int fieldNumber, final MessageLite value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeMessageNoTag(value); + } + + @Override + public void writeMessageSetExtension(final int fieldNumber, final MessageLite value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public void writeRawMessageSetExtension(final int fieldNumber, final ByteString value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public void writeMessageNoTag(final MessageLite value) throws IOException { + writeUInt32NoTag(value.getSerializedSize()); + value.writeTo(this); + } + + @Override + public void write(byte value) throws IOException { + if (position == limit) { + doFlush(); + } + + buffer(value); + } + + @Override + public void writeInt32NoTag(int value) throws IOException { + if (value >= 0) { + writeUInt32NoTag(value); + } else { + // Must sign-extend. + writeUInt64NoTag(value); + } + } + + @Override + public void writeUInt32NoTag(int value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE); + bufferUInt32NoTag(value); + } + + @Override + public void writeFixed32NoTag(final int value) throws IOException { + flushIfNotAvailable(FIXED_32_SIZE); + bufferFixed32NoTag(value); + } + + @Override + public void writeUInt64NoTag(long value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE); + bufferUInt64NoTag(value); + } + + @Override + public void writeFixed64NoTag(final long value) throws IOException { + flushIfNotAvailable(FIXED_64_SIZE); + bufferFixed64NoTag(value); + } + + @Override + public void writeStringNoTag(String value) throws IOException { + try { + // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()), + // and at most 3 times of it. We take advantage of this in both branches below. + final int maxLength = value.length() * Utf8.MAX_BYTES_PER_CHAR; + final int maxLengthVarIntSize = computeUInt32SizeNoTag(maxLength); + + // If we are streaming and the potential length is too big to fit in our buffer, we take the + // slower path. + if (maxLengthVarIntSize + maxLength > limit) { + // Allocate a byte[] that we know can fit the string and encode into it. String.getBytes() + // does the same internally and then does *another copy* to return a byte[] of exactly the + // right size. We can skip that copy and just writeRawBytes up to the actualLength of the + // UTF-8 encoded bytes. + final byte[] encodedBytes = new byte[maxLength]; + int actualLength = Utf8.encode(value, encodedBytes, 0, maxLength); + writeUInt32NoTag(actualLength); + writeLazy(encodedBytes, 0, actualLength); + return; + } + + // Fast path: we have enough space available in our buffer for the string... + if (maxLengthVarIntSize + maxLength > limit - position) { + // Flush to free up space. + doFlush(); + } + + // Optimize for the case where we know this length results in a constant varint length as + // this saves a pass for measuring the length of the string. + final int minLengthVarIntSize = computeUInt32SizeNoTag(value.length()); + int oldPosition = position; + final int length; + try { + if (minLengthVarIntSize == maxLengthVarIntSize) { + position = oldPosition + minLengthVarIntSize; + int newPosition = Utf8.encode(value, buffer, position, limit - position); + // Since this class is stateful and tracks the position, we rewind and store the + // state, prepend the length, then reset it back to the end of the string. + position = oldPosition; + length = newPosition - oldPosition - minLengthVarIntSize; + bufferUInt32NoTag(length); + position = newPosition; + } else { + length = Utf8.encodedLength(value); + bufferUInt32NoTag(length); + position = Utf8.encode(value, buffer, position, length); + } + totalBytesWritten += length; + } catch (UnpairedSurrogateException e) { + // Be extra careful and restore the original position for retrying the write with the + // less efficient path. + totalBytesWritten -= position - oldPosition; + position = oldPosition; + throw e; + } catch (ArrayIndexOutOfBoundsException e) { + throw new OutOfSpaceException(e); + } + } catch (UnpairedSurrogateException e) { + inefficientWriteStringNoTag(value, e); + } + } + + @Override + public void flush() throws IOException { + if (position > 0) { + // Flush the buffer. + doFlush(); + } + } + + @Override + public void write(byte[] value, int offset, int length) + throws IOException { + if (limit - position >= length) { + // We have room in the current buffer. + System.arraycopy(value, offset, buffer, position, length); + position += length; + totalBytesWritten += length; + } else { + // Write extends past current buffer. Fill the rest of this buffer and + // flush. + final int bytesWritten = limit - position; + System.arraycopy(value, offset, buffer, position, bytesWritten); + offset += bytesWritten; + length -= bytesWritten; + position = limit; + totalBytesWritten += bytesWritten; + doFlush(); + + // Now deal with the rest. + // Since we have an output stream, this is our buffer + // and buffer offset == 0 + if (length <= limit) { + // Fits in new buffer. + System.arraycopy(value, offset, buffer, 0, length); + position = length; + } else { + // Write is very big. Let's do it all at once. + out.write(value, offset, length); + } + totalBytesWritten += length; + } + } + + @Override + public void writeLazy(byte[] value, int offset, int length) throws IOException { + write(value, offset, length); + } + + @Override + public void write(ByteBuffer value) throws IOException { + int length = value.remaining(); + if (limit - position >= length) { + // We have room in the current buffer. + value.get(buffer, position, length); + position += length; + totalBytesWritten += length; + } else { + // Write extends past current buffer. Fill the rest of this buffer and + // flush. + final int bytesWritten = limit - position; + value.get(buffer, position, bytesWritten); + length -= bytesWritten; + position = limit; + totalBytesWritten += bytesWritten; + doFlush(); + + // Now deal with the rest. + // Since we have an output stream, this is our buffer + // and buffer offset == 0 + while (length > limit) { + // Copy data into the buffer before writing it to OutputStream. + value.get(buffer, 0, limit); + out.write(buffer, 0, limit); + length -= limit; + totalBytesWritten += limit; + } + value.get(buffer, 0, length); + position = length; + totalBytesWritten += length; + } + } + + @Override + public void writeLazy(ByteBuffer value) throws IOException { + write(value); + } + + private void flushIfNotAvailable(int requiredSize) throws IOException { + if (limit - position < requiredSize) { + doFlush(); + } + } + + private void doFlush() throws IOException { + out.write(buffer, 0, position); + position = 0; + } + } + + /** + * Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this + * platform. + */ + private static sun.misc.Unsafe getUnsafe() { + sun.misc.Unsafe unsafe = null; + try { + unsafe = AccessController.doPrivileged(new PrivilegedExceptionAction() { + @Override + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + + for (Field f : k.getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if (k.isInstance(x)) { + return k.cast(x); + } + } + // The sun.misc.Unsafe field does not exist. + return null; + } + }); + } catch (Throwable e) { + // Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError + // for Unsafe. + } + + logger.log(Level.FINEST, "sun.misc.Unsafe: {}", + unsafe != null ? "available" : "unavailable"); + return unsafe; + } + + /** + * Indicates whether or not unsafe array operations are supported on this platform. + */ + // TODO(nathanmittler): Add support for Android's MemoryBlock. + private static boolean supportsUnsafeArrayOperations() { + boolean supported = false; + if (UNSAFE != null) { + try { + UNSAFE.getClass().getMethod("arrayBaseOffset", Class.class); + UNSAFE.getClass().getMethod("putByte", Object.class, long.class, byte.class); + supported = true; + } catch (Throwable e) { + // Do nothing. + } + } + logger.log(Level.FINEST, "Unsafe array operations: {}", + supported ? "available" : "unavailable"); + return supported; + } + + /** + * Get the base offset for byte arrays, or {@code -1} if {@code sun.misc.Unsafe} is not + * available. + */ + private static int byteArrayBaseOffset() { + return HAS_UNSAFE_ARRAY_OPERATIONS ? UNSAFE.arrayBaseOffset(byte[].class) : -1; + } } 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 5e15cfbe6..e00ea342e 100644 --- a/java/core/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/core/src/main/java/com/google/protobuf/Descriptors.java @@ -74,16 +74,28 @@ public final class Descriptors { */ public static final class FileDescriptor extends GenericDescriptor { /** Convert the descriptor to its protocol message representation. */ - public FileDescriptorProto toProto() { return proto; } + @Override + public FileDescriptorProto toProto() { + return proto; + } /** Get the file name. */ - public String getName() { return proto.getName(); } + @Override + public String getName() { + return proto.getName(); + } /** Returns this object. */ - public FileDescriptor getFile() { return this; } + @Override + public FileDescriptor getFile() { + return this; + } /** Returns the same as getName(). */ - public String getFullName() { return proto.getName(); } + @Override + public String getFullName() { + return proto.getName(); + } /** * Get the proto package name. This is the package name given by the @@ -272,7 +284,7 @@ public final class Descriptors { * because a field has an undefined type or because two messages * were defined with the same name. */ - private static FileDescriptor buildFrom( + public static FileDescriptor buildFrom( final FileDescriptorProto proto, final FileDescriptor[] dependencies, final boolean allowUnknownDependencies) throws DescriptorValidationException { @@ -582,10 +594,16 @@ public final class Descriptors { public int getIndex() { return index; } /** Convert the descriptor to its protocol message representation. */ - public DescriptorProto toProto() { return proto; } + @Override + public DescriptorProto toProto() { + return proto; + } /** Get the type's unqualified name. */ - public String getName() { return proto.getName(); } + @Override + public String getName() { + return proto.getName(); + } /** * Get the type's fully-qualified name, within the proto language's @@ -598,10 +616,16 @@ public final class Descriptors { * * {@code Baz}'s full name is "foo.bar.Baz". */ - public String getFullName() { return fullName; } + @Override + public String getFullName() { + return fullName; + } /** Get the {@link FileDescriptor} containing this descriptor. */ - public FileDescriptor getFile() { return file; } + @Override + public FileDescriptor getFile() { + return file; + } /** If this is a nested type, get the outer descriptor, otherwise null. */ public Descriptor getContainingType() { return containingType; } @@ -875,19 +899,31 @@ public final class Descriptors { public int getIndex() { return index; } /** Convert the descriptor to its protocol message representation. */ - public FieldDescriptorProto toProto() { return proto; } + @Override + public FieldDescriptorProto toProto() { + return proto; + } /** Get the field's unqualified name. */ - public String getName() { return proto.getName(); } + @Override + public String getName() { + return proto.getName(); + } /** Get the field's number. */ - public int getNumber() { return proto.getNumber(); } + @Override + public int getNumber() { + return proto.getNumber(); + } /** * Get the field's fully-qualified name. * @see Descriptors.Descriptor#getFullName() */ - public String getFullName() { return fullName; } + @Override + public String getFullName() { + return fullName; + } /** Get the JSON name of this field. */ public String getJsonName() { @@ -901,17 +937,22 @@ public final class Descriptors { public JavaType getJavaType() { return type.getJavaType(); } /** For internal use only. */ + @Override public WireFormat.JavaType getLiteJavaType() { return getLiteType().getJavaType(); } /** Get the {@code FileDescriptor} containing this descriptor. */ - public FileDescriptor getFile() { return file; } + @Override + public FileDescriptor getFile() { + return file; + } /** Get the field's declared type. */ public Type getType() { return type; } /** For internal use only. */ + @Override public WireFormat.FieldType getLiteType() { return table[type.ordinal()]; } @@ -953,6 +994,7 @@ public final class Descriptors { } /** Is this field declared repeated? */ + @Override public boolean isRepeated() { return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED; } @@ -960,6 +1002,7 @@ public final class Descriptors { /** Does this field have the {@code [packed = true]} option or is this field * packable in proto3 and not explicitly setted to unpacked? */ + @Override public boolean isPacked() { if (!isPackable()) { return false; @@ -1048,6 +1091,7 @@ public final class Descriptors { } /** For enum fields, gets the field's type. */ + @Override public EnumDescriptor getEnumType() { if (getJavaType() != JavaType.ENUM) { throw new UnsupportedOperationException( @@ -1066,6 +1110,7 @@ public final class Descriptors { * @return negative, zero, or positive if {@code this} is less than, * equal to, or greater than {@code other}, respectively. */ + @Override public int compareTo(final FieldDescriptor other) { if (other.containingType != containingType) { throw new IllegalArgumentException( @@ -1123,7 +1168,7 @@ public final class Descriptors { private JavaType javaType; public FieldDescriptorProto.Type toProto() { - return FieldDescriptorProto.Type.valueOf(ordinal() + 1); + return FieldDescriptorProto.Type.forNumber(ordinal() + 1); } public JavaType getJavaType() { return javaType; } @@ -1466,8 +1511,8 @@ public final class Descriptors { * For internal use only. This is to satisfy the FieldDescriptorLite * interface. */ - public MessageLite.Builder internalMergeFrom( - MessageLite.Builder to, MessageLite from) { + @Override + public MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from) { // FieldDescriptors are only used with non-lite messages so we can just // down-cast and call mergeFrom directly. return ((Message.Builder) to).mergeFrom((Message) from); @@ -1487,19 +1532,31 @@ public final class Descriptors { public int getIndex() { return index; } /** Convert the descriptor to its protocol message representation. */ - public EnumDescriptorProto toProto() { return proto; } + @Override + public EnumDescriptorProto toProto() { + return proto; + } /** Get the type's unqualified name. */ - public String getName() { return proto.getName(); } + @Override + public String getName() { + return proto.getName(); + } /** * Get the type's fully-qualified name. * @see Descriptors.Descriptor#getFullName() */ - public String getFullName() { return fullName; } + @Override + public String getFullName() { + return fullName; + } /** Get the {@link FileDescriptor} containing this descriptor. */ - public FileDescriptor getFile() { return file; } + @Override + public FileDescriptor getFile() { + return file; + } /** If this is a nested type, get the outer descriptor, otherwise null. */ public Descriptor getContainingType() { return containingType; } @@ -1533,6 +1590,7 @@ public final class Descriptors { * @param number The value's number. * @return the value's descriptor, or {@code null} if not found. */ + @Override public EnumValueDescriptor findValueByNumber(final int number) { return file.pool.enumValuesByNumber.get( new DescriptorPool.DescriptorIntPair(this, number)); @@ -1659,13 +1717,22 @@ public final class Descriptors { public int getIndex() { return index; } /** Convert the descriptor to its protocol message representation. */ - public EnumValueDescriptorProto toProto() { return proto; } + @Override + public EnumValueDescriptorProto toProto() { + return proto; + } /** Get the value's unqualified name. */ - public String getName() { return proto.getName(); } + @Override + public String getName() { + return proto.getName(); + } /** Get the value's number. */ - public int getNumber() { return proto.getNumber(); } + @Override + public int getNumber() { + return proto.getNumber(); + } @Override public String toString() { return proto.getName(); } @@ -1674,10 +1741,16 @@ public final class Descriptors { * Get the value's fully-qualified name. * @see Descriptors.Descriptor#getFullName() */ - public String getFullName() { return fullName; } + @Override + public String getFullName() { + return fullName; + } /** Get the {@link FileDescriptor} containing this descriptor. */ - public FileDescriptor getFile() { return file; } + @Override + public FileDescriptor getFile() { + return file; + } /** Get the value's enum type. */ public EnumDescriptor getType() { return type; } @@ -1745,19 +1818,31 @@ public final class Descriptors { public int getIndex() { return index; } /** Convert the descriptor to its protocol message representation. */ - public ServiceDescriptorProto toProto() { return proto; } + @Override + public ServiceDescriptorProto toProto() { + return proto; + } /** Get the type's unqualified name. */ - public String getName() { return proto.getName(); } + @Override + public String getName() { + return proto.getName(); + } /** * Get the type's fully-qualified name. * @see Descriptors.Descriptor#getFullName() */ - public String getFullName() { return fullName; } + @Override + public String getFullName() { + return fullName; + } /** Get the {@link FileDescriptor} containing this descriptor. */ - public FileDescriptor getFile() { return file; } + @Override + public FileDescriptor getFile() { + return file; + } /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */ public ServiceOptions getOptions() { return proto.getOptions(); } @@ -1835,19 +1920,31 @@ public final class Descriptors { public int getIndex() { return index; } /** Convert the descriptor to its protocol message representation. */ - public MethodDescriptorProto toProto() { return proto; } + @Override + public MethodDescriptorProto toProto() { + return proto; + } /** Get the method's unqualified name. */ - public String getName() { return proto.getName(); } + @Override + public String getName() { + return proto.getName(); + } /** * Get the method's fully-qualified name. * @see Descriptors.Descriptor#getFullName() */ - public String getFullName() { return fullName; } + @Override + public String getFullName() { + return fullName; + } /** Get the {@link FileDescriptor} containing this descriptor. */ - public FileDescriptor getFile() { return file; } + @Override + public FileDescriptor getFile() { + return file; + } /** Get the method's service type. */ public ServiceDescriptor getService() { return service; } @@ -2248,10 +2345,22 @@ public final class Descriptors { * that has the same name as an existing package. */ private static final class PackageDescriptor extends GenericDescriptor { - public Message toProto() { return file.toProto(); } - public String getName() { return name; } - public String getFullName() { return fullName; } - public FileDescriptor getFile() { return file; } + @Override + public Message toProto() { + return file.toProto(); + } + @Override + public String getName() { + return name; + } + @Override + public String getFullName() { + return fullName; + } + @Override + public FileDescriptor getFile() { + return file; + } PackageDescriptor(final String name, final String fullName, final FileDescriptor file) { diff --git a/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java b/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java index bcc9d6ee9..a9543b837 100644 --- a/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java +++ b/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java @@ -34,7 +34,6 @@ import com.google.protobuf.Internal.DoubleList; import java.util.Arrays; import java.util.Collection; -import java.util.List; import java.util.RandomAccess; /** @@ -45,8 +44,6 @@ import java.util.RandomAccess; final class DoubleArrayList extends AbstractProtobufList implements DoubleList, RandomAccess { - private static final int DEFAULT_CAPACITY = 10; - private static final DoubleArrayList EMPTY_LIST = new DoubleArrayList(); static { EMPTY_LIST.makeImmutable(); @@ -71,32 +68,56 @@ final class DoubleArrayList * Constructs a new mutable {@code DoubleArrayList} with default capacity. */ DoubleArrayList() { - this(DEFAULT_CAPACITY); - } - - /** - * Constructs a new mutable {@code DoubleArrayList} with the provided capacity. - */ - DoubleArrayList(int capacity) { - array = new double[capacity]; - size = 0; + this(new double[DEFAULT_CAPACITY], 0); } /** * Constructs a new mutable {@code DoubleArrayList} containing the same elements as {@code other}. */ - DoubleArrayList(List other) { - if (other instanceof DoubleArrayList) { - DoubleArrayList list = (DoubleArrayList) other; - array = list.array.clone(); - size = list.size; - } else { - size = other.size(); - array = new double[size]; - for (int i = 0; i < size; i++) { - array[i] = other.get(i); + private DoubleArrayList(double[] array, int size) { + this.array = array; + this.size = size; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof DoubleArrayList)) { + return super.equals(o); + } + DoubleArrayList other = (DoubleArrayList) o; + if (size != other.size) { + return false; + } + + final double[] arr = other.array; + for (int i = 0; i < size; i++) { + if (array[i] != arr[i]) { + return false; } } + + return true; + } + + @Override + public int hashCode() { + int result = 1; + for (int i = 0; i < size; i++) { + long bits = Double.doubleToLongBits(array[i]); + result = (31 * result) + Internal.hashLong(bits); + } + return result; + } + + @Override + public DoubleList mutableCopyWithCapacity(int capacity) { + if (capacity < size) { + throw new IllegalArgumentException(); + } + return new DoubleArrayList(Arrays.copyOf(array, capacity), size); } @Override diff --git a/java/core/src/main/java/com/google/protobuf/DynamicMessage.java b/java/core/src/main/java/com/google/protobuf/DynamicMessage.java index 3ea1b6889..859a9e8fc 100644 --- a/java/core/src/main/java/com/google/protobuf/DynamicMessage.java +++ b/java/core/src/main/java/com/google/protobuf/DynamicMessage.java @@ -156,18 +156,22 @@ public final class DynamicMessage extends AbstractMessage { // ----------------------------------------------------------------- // Implementation of Message interface. + @Override public Descriptor getDescriptorForType() { return type; } + @Override public DynamicMessage getDefaultInstanceForType() { return getDefaultInstance(type); } + @Override public Map getAllFields() { return fields.getAllFields(); } + @Override public boolean hasOneof(OneofDescriptor oneof) { verifyOneofContainingType(oneof); FieldDescriptor field = oneofCases[oneof.getIndex()]; @@ -177,16 +181,19 @@ public final class DynamicMessage extends AbstractMessage { return true; } + @Override public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) { verifyOneofContainingType(oneof); return oneofCases[oneof.getIndex()]; } + @Override public boolean hasField(FieldDescriptor field) { verifyContainingType(field); return fields.hasField(field); } + @Override public Object getField(FieldDescriptor field) { verifyContainingType(field); Object result = fields.getField(field); @@ -202,16 +209,19 @@ public final class DynamicMessage extends AbstractMessage { return result; } + @Override public int getRepeatedFieldCount(FieldDescriptor field) { verifyContainingType(field); return fields.getRepeatedFieldCount(field); } + @Override public Object getRepeatedField(FieldDescriptor field, int index) { verifyContainingType(field); return fields.getRepeatedField(field, index); } + @Override public UnknownFieldSet getUnknownFields() { return unknownFields; } @@ -264,19 +274,22 @@ public final class DynamicMessage extends AbstractMessage { return size; } + @Override public Builder newBuilderForType() { return new Builder(type); } + @Override public Builder toBuilder() { return newBuilderForType().mergeFrom(this); } + @Override public Parser getParserForType() { return new AbstractParser() { + @Override public DynamicMessage parsePartialFrom( - CodedInputStream input, - ExtensionRegistryLite extensionRegistry) + CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { Builder builder = newBuilder(type); try { @@ -370,6 +383,7 @@ public final class DynamicMessage extends AbstractMessage { } } + @Override public DynamicMessage build() { if (!isInitialized()) { throw newUninitializedMessageException( @@ -394,6 +408,7 @@ public final class DynamicMessage extends AbstractMessage { return buildPartial(); } + @Override public DynamicMessage buildPartial() { fields.makeImmutable(); DynamicMessage result = @@ -411,22 +426,27 @@ public final class DynamicMessage extends AbstractMessage { return result; } + @Override public boolean isInitialized() { return DynamicMessage.isInitialized(type, fields); } + @Override public Descriptor getDescriptorForType() { return type; } + @Override public DynamicMessage getDefaultInstanceForType() { return getDefaultInstance(type); } + @Override public Map getAllFields() { return fields.getAllFields(); } + @Override public Builder newBuilderForField(FieldDescriptor field) { verifyContainingType(field); @@ -438,6 +458,7 @@ public final class DynamicMessage extends AbstractMessage { return new Builder(field.getMessageType()); } + @Override public boolean hasOneof(OneofDescriptor oneof) { verifyOneofContainingType(oneof); FieldDescriptor field = oneofCases[oneof.getIndex()]; @@ -447,11 +468,13 @@ public final class DynamicMessage extends AbstractMessage { return true; } + @Override public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) { verifyOneofContainingType(oneof); return oneofCases[oneof.getIndex()]; } + @Override public Builder clearOneof(OneofDescriptor oneof) { verifyOneofContainingType(oneof); FieldDescriptor field = oneofCases[oneof.getIndex()]; @@ -461,11 +484,13 @@ public final class DynamicMessage extends AbstractMessage { return this; } + @Override public boolean hasField(FieldDescriptor field) { verifyContainingType(field); return fields.hasField(field); } + @Override public Object getField(FieldDescriptor field) { verifyContainingType(field); Object result = fields.getField(field); @@ -481,6 +506,7 @@ public final class DynamicMessage extends AbstractMessage { return result; } + @Override public Builder setField(FieldDescriptor field, Object value) { verifyContainingType(field); ensureIsMutable(); @@ -505,6 +531,7 @@ public final class DynamicMessage extends AbstractMessage { return this; } + @Override public Builder clearField(FieldDescriptor field) { verifyContainingType(field); ensureIsMutable(); @@ -519,24 +546,27 @@ public final class DynamicMessage extends AbstractMessage { return this; } + @Override public int getRepeatedFieldCount(FieldDescriptor field) { verifyContainingType(field); return fields.getRepeatedFieldCount(field); } + @Override public Object getRepeatedField(FieldDescriptor field, int index) { verifyContainingType(field); return fields.getRepeatedField(field, index); } - public Builder setRepeatedField(FieldDescriptor field, - int index, Object value) { + @Override + public Builder setRepeatedField(FieldDescriptor field, int index, Object value) { verifyContainingType(field); ensureIsMutable(); fields.setRepeatedField(field, index, value); return this; } + @Override public Builder addRepeatedField(FieldDescriptor field, Object value) { verifyContainingType(field); ensureIsMutable(); @@ -544,10 +574,12 @@ public final class DynamicMessage extends AbstractMessage { return this; } + @Override public UnknownFieldSet getUnknownFields() { return unknownFields; } + @Override public Builder setUnknownFields(UnknownFieldSet unknownFields) { if (getDescriptorForType().getFile().getSyntax() == Descriptors.FileDescriptor.Syntax.PROTO3) { diff --git a/java/core/src/main/java/com/google/protobuf/ExperimentalApi.java b/java/core/src/main/java/com/google/protobuf/ExperimentalApi.java index 6f41fb81a..3cd4c8849 100644 --- a/java/core/src/main/java/com/google/protobuf/ExperimentalApi.java +++ b/java/core/src/main/java/com/google/protobuf/ExperimentalApi.java @@ -1,3 +1,33 @@ +// 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. + package com.google.protobuf; import java.lang.annotation.Documented; diff --git a/java/core/src/main/java/com/google/protobuf/Extension.java b/java/core/src/main/java/com/google/protobuf/Extension.java index 68d29f33d..08ec5b454 100644 --- a/java/core/src/main/java/com/google/protobuf/Extension.java +++ b/java/core/src/main/java/com/google/protobuf/Extension.java @@ -42,6 +42,7 @@ public abstract class Extension public abstract Descriptors.FieldDescriptor getDescriptor(); /** Returns whether or not this extension is a Lite Extension. */ + @Override final boolean isLite() { return false; } diff --git a/java/core/src/main/java/com/google/protobuf/FieldSet.java b/java/core/src/main/java/com/google/protobuf/FieldSet.java index 47924b657..4e89709f3 100644 --- a/java/core/src/main/java/com/google/protobuf/FieldSet.java +++ b/java/core/src/main/java/com/google/protobuf/FieldSet.java @@ -120,6 +120,25 @@ final class FieldSet other = (FieldSet) o; + return other.fields.equals(other.fields); + } + + @Override + public int hashCode() { + return fields.hashCode(); + } /** * Clones the FieldSet. The returned FieldSet will be mutable even if the diff --git a/java/core/src/main/java/com/google/protobuf/FloatArrayList.java b/java/core/src/main/java/com/google/protobuf/FloatArrayList.java index 033b5eed3..63cb6d773 100644 --- a/java/core/src/main/java/com/google/protobuf/FloatArrayList.java +++ b/java/core/src/main/java/com/google/protobuf/FloatArrayList.java @@ -34,7 +34,6 @@ import com.google.protobuf.Internal.FloatList; import java.util.Arrays; import java.util.Collection; -import java.util.List; import java.util.RandomAccess; /** @@ -44,8 +43,6 @@ import java.util.RandomAccess; */ final class FloatArrayList extends AbstractProtobufList implements FloatList, RandomAccess { - private static final int DEFAULT_CAPACITY = 10; - private static final FloatArrayList EMPTY_LIST = new FloatArrayList(); static { EMPTY_LIST.makeImmutable(); @@ -70,32 +67,55 @@ final class FloatArrayList extends AbstractProtobufList implements FloatL * Constructs a new mutable {@code FloatArrayList} with default capacity. */ FloatArrayList() { - this(DEFAULT_CAPACITY); - } - - /** - * Constructs a new mutable {@code FloatArrayList} with the provided capacity. - */ - FloatArrayList(int capacity) { - array = new float[capacity]; - size = 0; + this(new float[DEFAULT_CAPACITY], 0); } /** * Constructs a new mutable {@code FloatArrayList} containing the same elements as {@code other}. */ - FloatArrayList(List other) { - if (other instanceof FloatArrayList) { - FloatArrayList list = (FloatArrayList) other; - array = list.array.clone(); - size = list.size; - } else { - size = other.size(); - array = new float[size]; - for (int i = 0; i < size; i++) { - array[i] = other.get(i); + private FloatArrayList(float[] array, int size) { + this.array = array; + this.size = size; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof FloatArrayList)) { + return super.equals(o); + } + FloatArrayList other = (FloatArrayList) o; + if (size != other.size) { + return false; + } + + final float[] arr = other.array; + for (int i = 0; i < size; i++) { + if (array[i] != arr[i]) { + return false; } } + + return true; + } + + @Override + public int hashCode() { + int result = 1; + for (int i = 0; i < size; i++) { + result = (31 * result) + Float.floatToIntBits(array[i]); + } + return result; + } + + @Override + public FloatList mutableCopyWithCapacity(int capacity) { + if (capacity < size) { + throw new IllegalArgumentException(); + } + return new FloatArrayList(Arrays.copyOf(array, capacity), size); } @Override diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java index ceb97a4e7..790cb622a 100644 --- a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java +++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -80,6 +80,7 @@ public abstract class GeneratedMessage extends AbstractMessage unknownFields = builder.getUnknownFields(); } + @Override public Parser getParserForType() { throw new UnsupportedOperationException( "This is supposed to be overridden by subclasses."); @@ -102,7 +103,7 @@ public abstract class GeneratedMessage extends AbstractMessage */ protected abstract FieldAccessorTable internalGetFieldAccessorTable(); - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public Descriptor getDescriptorForType() { return internalGetFieldAccessorTable().descriptor; } @@ -191,7 +192,7 @@ public abstract class GeneratedMessage extends AbstractMessage return true; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public Map getAllFields() { return Collections.unmodifiableMap( getAllFieldsMutable(/* getBytesForString = */ false)); @@ -212,22 +213,22 @@ public abstract class GeneratedMessage extends AbstractMessage getAllFieldsMutable(/* getBytesForString = */ true)); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public boolean hasOneof(final OneofDescriptor oneof) { return internalGetFieldAccessorTable().getOneof(oneof).has(this); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public FieldDescriptor getOneofFieldDescriptor(final OneofDescriptor oneof) { return internalGetFieldAccessorTable().getOneof(oneof).get(this); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public boolean hasField(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field).has(this); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public Object getField(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field).get(this); } @@ -244,19 +245,19 @@ public abstract class GeneratedMessage extends AbstractMessage return internalGetFieldAccessorTable().getField(field).getRaw(this); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public int getRepeatedFieldCount(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field) .getRepeatedCount(this); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public Object getRepeatedField(final FieldDescriptor field, final int index) { return internalGetFieldAccessorTable().getField(field) .getRepeated(this, index); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public UnknownFieldSet getUnknownFields() { throw new UnsupportedOperationException( "This is supposed to be overridden by subclasses."); @@ -380,7 +381,7 @@ public abstract class GeneratedMessage extends AbstractMessage } @SuppressWarnings("unchecked") - public abstract static class Builder + public abstract static class Builder > extends AbstractMessage.Builder { private BuilderParent builderParent; @@ -444,6 +445,7 @@ public abstract class GeneratedMessage extends AbstractMessage * Called by the initialization and clear code paths to allow subclasses to * reset any of their builtin fields back to the initial values. */ + @Override public BuilderType clear() { unknownFields = UnknownFieldSet.getDefaultInstance(); onChanged(); @@ -457,12 +459,12 @@ public abstract class GeneratedMessage extends AbstractMessage */ protected abstract FieldAccessorTable internalGetFieldAccessorTable(); - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public Descriptor getDescriptorForType() { return internalGetFieldAccessorTable().descriptor; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public Map getAllFields() { return Collections.unmodifiableMap(getAllFieldsMutable()); } @@ -510,39 +512,38 @@ public abstract class GeneratedMessage extends AbstractMessage return result; } - public Message.Builder newBuilderForField( - final FieldDescriptor field) { + @Override + public Message.Builder newBuilderForField(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field).newBuilder(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public Message.Builder getFieldBuilder(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field).getBuilder(this); } - //@Override (Java 1.6 override semantics, but we must support 1.5) - public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, - int index) { + @Override + public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, int index) { return internalGetFieldAccessorTable().getField(field).getRepeatedBuilder( this, index); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public boolean hasOneof(final OneofDescriptor oneof) { return internalGetFieldAccessorTable().getOneof(oneof).has(this); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public FieldDescriptor getOneofFieldDescriptor(final OneofDescriptor oneof) { return internalGetFieldAccessorTable().getOneof(oneof).get(this); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public boolean hasField(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field).has(this); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public Object getField(final FieldDescriptor field) { Object object = internalGetFieldAccessorTable().getField(field).get(this); if (field.isRepeated()) { @@ -554,52 +555,52 @@ public abstract class GeneratedMessage extends AbstractMessage } } - public BuilderType setField(final FieldDescriptor field, - final Object value) { + @Override + public BuilderType setField(final FieldDescriptor field, final Object value) { internalGetFieldAccessorTable().getField(field).set(this, value); return (BuilderType) this; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public BuilderType clearField(final FieldDescriptor field) { internalGetFieldAccessorTable().getField(field).clear(this); return (BuilderType) this; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public BuilderType clearOneof(final OneofDescriptor oneof) { internalGetFieldAccessorTable().getOneof(oneof).clear(this); return (BuilderType) this; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public int getRepeatedFieldCount(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field) .getRepeatedCount(this); } - //@Override (Java 1.6 override semantics, but we must support 1.5) - public Object getRepeatedField(final FieldDescriptor field, - final int index) { + @Override + public Object getRepeatedField(final FieldDescriptor field, final int index) { return internalGetFieldAccessorTable().getField(field) .getRepeated(this, index); } - public BuilderType setRepeatedField(final FieldDescriptor field, - final int index, final Object value) { + @Override + public BuilderType setRepeatedField( + final FieldDescriptor field, final int index, final Object value) { internalGetFieldAccessorTable().getField(field) .setRepeated(this, index, value); return (BuilderType) this; } - public BuilderType addRepeatedField(final FieldDescriptor field, - final Object value) { + @Override + public BuilderType addRepeatedField(final FieldDescriptor field, final Object value) { internalGetFieldAccessorTable().getField(field).addRepeated(this, value); return (BuilderType) this; } - public BuilderType setUnknownFields( - final UnknownFieldSet unknownFields) { + @Override + public BuilderType setUnknownFields(final UnknownFieldSet unknownFields) { this.unknownFields = unknownFields; onChanged(); return (BuilderType) this; @@ -616,7 +617,7 @@ public abstract class GeneratedMessage extends AbstractMessage return (BuilderType) this; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public boolean isInitialized() { for (final FieldDescriptor field : getDescriptorForType().getFields()) { // Check that all required fields are present. @@ -646,7 +647,7 @@ public abstract class GeneratedMessage extends AbstractMessage return true; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public final UnknownFieldSet getUnknownFields() { return unknownFields; } @@ -670,7 +671,7 @@ public abstract class GeneratedMessage extends AbstractMessage */ private class BuilderParentImpl implements BuilderParent { - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void markDirty() { onChanged(); } @@ -735,6 +736,7 @@ public abstract class GeneratedMessage extends AbstractMessage public interface ExtendableMessageOrBuilder< MessageType extends ExtendableMessage> extends MessageOrBuilder { // Re-define for return type covariance. + @Override Message getDefaultInstanceForType(); /** Check if a singular extension is present. */ @@ -795,6 +797,8 @@ public abstract class GeneratedMessage extends AbstractMessage extends GeneratedMessage implements ExtendableMessageOrBuilder { + private static final long serialVersionUID = 1L; + private final FieldSet extensions; protected ExtendableMessage() { @@ -821,9 +825,8 @@ public abstract class GeneratedMessage extends AbstractMessage } /** Check if a singular extension is present. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) - public final boolean hasExtension( - final ExtensionLite extensionLite) { + @Override + public final boolean hasExtension(final ExtensionLite extensionLite) { Extension extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); @@ -831,7 +834,7 @@ public abstract class GeneratedMessage extends AbstractMessage } /** Get the number of elements in a repeated extension. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public final int getExtensionCount( final ExtensionLite> extensionLite) { Extension> extension = checkNotLite(extensionLite); @@ -842,10 +845,9 @@ public abstract class GeneratedMessage extends AbstractMessage } /** Get the value of an extension. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override @SuppressWarnings("unchecked") - public final Type getExtension( - final ExtensionLite extensionLite) { + public final Type getExtension(final ExtensionLite extensionLite) { Extension extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); @@ -867,11 +869,10 @@ public abstract class GeneratedMessage extends AbstractMessage } /** Get one element of a repeated extension. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override @SuppressWarnings("unchecked") public final Type getExtension( - final ExtensionLite> extensionLite, - final int index) { + final ExtensionLite> extensionLite, final int index) { Extension> extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); @@ -1019,7 +1020,9 @@ public abstract class GeneratedMessage extends AbstractMessage verifyContainingType(field); final Object value = extensions.getField(field); if (value == null) { - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.isRepeated()) { + return Collections.emptyList(); + } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { // Lacking an ExtensionRegistry, we have no way to determine the // extension's real type, so we return a DynamicMessage. return DynamicMessage.getDefaultInstance(field.getMessageType()); @@ -1103,7 +1106,7 @@ public abstract class GeneratedMessage extends AbstractMessage @SuppressWarnings("unchecked") public abstract static class ExtendableBuilder< MessageType extends ExtendableMessage, - BuilderType extends ExtendableBuilder> + BuilderType extends ExtendableBuilder> extends Builder implements ExtendableMessageOrBuilder { @@ -1155,9 +1158,8 @@ public abstract class GeneratedMessage extends AbstractMessage } /** Check if a singular extension is present. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) - public final boolean hasExtension( - final ExtensionLite extensionLite) { + @Override + public final boolean hasExtension(final ExtensionLite extensionLite) { Extension extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); @@ -1165,7 +1167,7 @@ public abstract class GeneratedMessage extends AbstractMessage } /** Get the number of elements in a repeated extension. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public final int getExtensionCount( final ExtensionLite> extensionLite) { Extension> extension = checkNotLite(extensionLite); @@ -1176,9 +1178,8 @@ public abstract class GeneratedMessage extends AbstractMessage } /** Get the value of an extension. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) - public final Type getExtension( - final ExtensionLite extensionLite) { + @Override + public final Type getExtension(final ExtensionLite extensionLite) { Extension extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); @@ -1200,10 +1201,9 @@ public abstract class GeneratedMessage extends AbstractMessage } /** Get one element of a repeated extension. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public final Type getExtension( - final ExtensionLite> extensionLite, - final int index) { + final ExtensionLite> extensionLite, final int index) { Extension> extension = checkNotLite(extensionLite); verifyExtensionContainingType(extension); @@ -1456,10 +1456,9 @@ public abstract class GeneratedMessage extends AbstractMessage // obtained. return new GeneratedExtension( new CachedDescriptorRetriever() { - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public FieldDescriptor loadDescriptor() { - return scope.getDescriptorForType().getExtensions() - .get(descriptorIndex); + return scope.getDescriptorForType().getExtensions().get(descriptorIndex); } }, singularType, @@ -1487,6 +1486,7 @@ public abstract class GeneratedMessage extends AbstractMessage private volatile FieldDescriptor descriptor; protected abstract FieldDescriptor loadDescriptor(); + @Override public FieldDescriptor getDescriptor() { if (descriptor == null) { synchronized (this) { @@ -1516,6 +1516,7 @@ public abstract class GeneratedMessage extends AbstractMessage // obtained. return new GeneratedExtension( new CachedDescriptorRetriever() { + @Override protected FieldDescriptor loadDescriptor() { return scope.getDescriptorForType().findFieldByName(name); } @@ -1542,17 +1543,18 @@ public abstract class GeneratedMessage extends AbstractMessage // used to obtain the extension's FieldDescriptor. return new GeneratedExtension( new CachedDescriptorRetriever() { + @Override protected FieldDescriptor loadDescriptor() { try { - Class clazz = - singularType.getClassLoader().loadClass(descriptorOuterClass); - FileDescriptor file = - (FileDescriptor) clazz.getField("descriptor").get(null); + Class clazz = singularType.getClassLoader().loadClass(descriptorOuterClass); + FileDescriptor file = (FileDescriptor) clazz.getField("descriptor").get(null); return file.findExtensionByName(extensionName); } catch (Exception e) { throw new RuntimeException( - "Cannot load descriptors: " + descriptorOuterClass + - " is not a valid descriptor class name", e); + "Cannot load descriptors: " + + descriptorOuterClass + + " is not a valid descriptor class name", + e); } } }, @@ -1634,12 +1636,13 @@ public abstract class GeneratedMessage extends AbstractMessage if (descriptorRetriever != null) { throw new IllegalStateException("Already initialized."); } - descriptorRetriever = new ExtensionDescriptorRetriever() { - //@Override (Java 1.6 override semantics, but we must support 1.5) - public FieldDescriptor getDescriptor() { - return descriptor; - } - }; + descriptorRetriever = + new ExtensionDescriptorRetriever() { + @Override + public FieldDescriptor getDescriptor() { + return descriptor; + } + }; } private ExtensionDescriptorRetriever descriptorRetriever; @@ -1649,6 +1652,7 @@ public abstract class GeneratedMessage extends AbstractMessage private final Method enumGetValueDescriptor; private final ExtensionType extensionType; + @Override public FieldDescriptor getDescriptor() { if (descriptorRetriever == null) { throw new IllegalStateException( @@ -1661,10 +1665,12 @@ public abstract class GeneratedMessage extends AbstractMessage * If the extension is an embedded message or group, returns the default * instance of the message. */ + @Override public Message getMessageDefaultInstance() { return messageDefaultInstance; } + @Override protected ExtensionType getExtensionType() { return extensionType; } @@ -1675,7 +1681,7 @@ public abstract class GeneratedMessage extends AbstractMessage * EnumValueDescriptors but the native accessors use the generated enum * type. */ - // @Override + @Override @SuppressWarnings("unchecked") protected Object fromReflectionType(final Object value) { FieldDescriptor descriptor = getDescriptor(); @@ -1700,7 +1706,7 @@ public abstract class GeneratedMessage extends AbstractMessage * Like {@link #fromReflectionType(Object)}, but if the type is a repeated * type, this converts a single element. */ - // @Override + @Override protected Object singularFromReflectionType(final Object value) { FieldDescriptor descriptor = getDescriptor(); switch (descriptor.getJavaType()) { @@ -1724,7 +1730,7 @@ public abstract class GeneratedMessage extends AbstractMessage * EnumValueDescriptors but the native accessors use the generated enum * type. */ - // @Override + @Override @SuppressWarnings("unchecked") protected Object toReflectionType(final Object value) { FieldDescriptor descriptor = getDescriptor(); @@ -1748,7 +1754,7 @@ public abstract class GeneratedMessage extends AbstractMessage * Like {@link #toReflectionType(Object)}, but if the type is a repeated * type, this converts a single element. */ - // @Override + @Override protected Object singularToReflectionType(final Object value) { FieldDescriptor descriptor = getDescriptor(); switch (descriptor.getJavaType()) { @@ -1759,22 +1765,22 @@ public abstract class GeneratedMessage extends AbstractMessage } } - // @Override + @Override public int getNumber() { return getDescriptor().getNumber(); } - // @Override + @Override public WireFormat.FieldType getLiteType() { return getDescriptor().getLiteType(); } - // @Override + @Override public boolean isRepeated() { return getDescriptor().isRepeated(); } - // @Override + @Override @SuppressWarnings("unchecked") public Type getDefaultValue() { if (isRepeated()) { @@ -2124,49 +2130,57 @@ public abstract class GeneratedMessage extends AbstractMessage return ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber(); } + @Override public Object get(final GeneratedMessage message) { return invokeOrDie(getMethod, message); } + @Override public Object get(GeneratedMessage.Builder builder) { return invokeOrDie(getMethodBuilder, builder); } + @Override public Object getRaw(final GeneratedMessage message) { return get(message); } + @Override public Object getRaw(GeneratedMessage.Builder builder) { return get(builder); } + @Override public void set(final Builder builder, final Object value) { invokeOrDie(setMethod, builder, value); } - public Object getRepeated(final GeneratedMessage message, - final int index) { + @Override + public Object getRepeated(final GeneratedMessage message, final int index) { throw new UnsupportedOperationException( "getRepeatedField() called on a singular field."); } - public Object getRepeatedRaw(final GeneratedMessage message, - final int index) { + @Override + public Object getRepeatedRaw(final GeneratedMessage message, final int index) { throw new UnsupportedOperationException( "getRepeatedFieldRaw() called on a singular field."); } + @Override public Object getRepeated(GeneratedMessage.Builder builder, int index) { throw new UnsupportedOperationException( "getRepeatedField() called on a singular field."); } - public Object getRepeatedRaw(GeneratedMessage.Builder builder, - int index) { + @Override + public Object getRepeatedRaw(GeneratedMessage.Builder builder, int index) { throw new UnsupportedOperationException( "getRepeatedFieldRaw() called on a singular field."); } - public void setRepeated(final Builder builder, final int index, - final Object value) { + @Override + public void setRepeated(final Builder builder, final int index, final Object value) { throw new UnsupportedOperationException( "setRepeatedField() called on a singular field."); } + @Override public void addRepeated(final Builder builder, final Object value) { throw new UnsupportedOperationException( "addRepeatedField() called on a singular field."); } + @Override public boolean has(final GeneratedMessage message) { if (!hasHasMethod) { if (isOneofField) { @@ -2176,6 +2190,7 @@ public abstract class GeneratedMessage extends AbstractMessage } return (Boolean) invokeOrDie(hasMethod, message); } + @Override public boolean has(GeneratedMessage.Builder builder) { if (!hasHasMethod) { if (isOneofField) { @@ -2185,27 +2200,32 @@ public abstract class GeneratedMessage extends AbstractMessage } return (Boolean) invokeOrDie(hasMethodBuilder, builder); } + @Override public int getRepeatedCount(final GeneratedMessage message) { throw new UnsupportedOperationException( "getRepeatedFieldSize() called on a singular field."); } + @Override public int getRepeatedCount(GeneratedMessage.Builder builder) { throw new UnsupportedOperationException( "getRepeatedFieldSize() called on a singular field."); } + @Override public void clear(final Builder builder) { invokeOrDie(clearMethod, builder); } + @Override public Message.Builder newBuilder() { throw new UnsupportedOperationException( "newBuilderForField() called on a non-Message type."); } + @Override public Message.Builder getBuilder(GeneratedMessage.Builder builder) { throw new UnsupportedOperationException( "getFieldBuilder() called on a non-Message type."); } - public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, - int index) { + @Override + public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, int index) { throw new UnsupportedOperationException( "getRepeatedFieldBuilder() called on a non-Message type."); } @@ -2249,18 +2269,23 @@ public abstract class GeneratedMessage extends AbstractMessage clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); } + @Override public Object get(final GeneratedMessage message) { return invokeOrDie(getMethod, message); } + @Override public Object get(GeneratedMessage.Builder builder) { return invokeOrDie(getMethodBuilder, builder); } + @Override public Object getRaw(final GeneratedMessage message) { return get(message); } + @Override public Object getRaw(GeneratedMessage.Builder builder) { return get(builder); } + @Override public void set(final Builder builder, final Object value) { // Add all the elements individually. This serves two purposes: // 1) Verifies that each element has the correct type. @@ -2271,54 +2296,64 @@ public abstract class GeneratedMessage extends AbstractMessage addRepeated(builder, element); } } - public Object getRepeated(final GeneratedMessage message, - final int index) { + @Override + public Object getRepeated(final GeneratedMessage message, final int index) { return invokeOrDie(getRepeatedMethod, message, index); } + @Override public Object getRepeated(GeneratedMessage.Builder builder, int index) { return invokeOrDie(getRepeatedMethodBuilder, builder, index); } + @Override public Object getRepeatedRaw(GeneratedMessage message, int index) { return getRepeated(message, index); } - public Object getRepeatedRaw(GeneratedMessage.Builder builder, - int index) { + @Override + public Object getRepeatedRaw(GeneratedMessage.Builder builder, int index) { return getRepeated(builder, index); } - public void setRepeated(final Builder builder, - final int index, final Object value) { + @Override + public void setRepeated(final Builder builder, final int index, final Object value) { invokeOrDie(setRepeatedMethod, builder, index, value); } + @Override public void addRepeated(final Builder builder, final Object value) { invokeOrDie(addRepeatedMethod, builder, value); } + @Override public boolean has(final GeneratedMessage message) { throw new UnsupportedOperationException( "hasField() called on a repeated field."); } + @Override public boolean has(GeneratedMessage.Builder builder) { throw new UnsupportedOperationException( "hasField() called on a repeated field."); } + @Override public int getRepeatedCount(final GeneratedMessage message) { return (Integer) invokeOrDie(getCountMethod, message); } + @Override public int getRepeatedCount(GeneratedMessage.Builder builder) { return (Integer) invokeOrDie(getCountMethodBuilder, builder); } + @Override public void clear(final Builder builder) { invokeOrDie(clearMethod, builder); } + @Override public Message.Builder newBuilder() { throw new UnsupportedOperationException( "newBuilderForField() called on a non-Message type."); } + @Override public Message.Builder getBuilder(GeneratedMessage.Builder builder) { throw new UnsupportedOperationException( "getFieldBuilder() called on a non-Message type."); } - public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, - int index) { + @Override + public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, int index) { throw new UnsupportedOperationException( "getRepeatedFieldBuilder() called on a non-Message type."); } @@ -2355,6 +2390,7 @@ public abstract class GeneratedMessage extends AbstractMessage field.getNumber()); } + @Override public Object get(GeneratedMessage message) { List result = new ArrayList(); for (int i = 0; i < getRepeatedCount(message); i++) { @@ -2363,6 +2399,7 @@ public abstract class GeneratedMessage extends AbstractMessage return Collections.unmodifiableList(result); } + @Override public Object get(Builder builder) { List result = new ArrayList(); for (int i = 0; i < getRepeatedCount(builder); i++) { @@ -2371,14 +2408,17 @@ public abstract class GeneratedMessage extends AbstractMessage return Collections.unmodifiableList(result); } + @Override public Object getRaw(GeneratedMessage message) { return get(message); } + @Override public Object getRaw(GeneratedMessage.Builder builder) { return get(builder); } + @Override public void set(Builder builder, Object value) { clear(builder); for (Object entry : (List) value) { @@ -2386,63 +2426,76 @@ public abstract class GeneratedMessage extends AbstractMessage } } + @Override public Object getRepeated(GeneratedMessage message, int index) { return getMapField(message).getList().get(index); } + @Override public Object getRepeated(Builder builder, int index) { return getMapField(builder).getList().get(index); } + @Override public Object getRepeatedRaw(GeneratedMessage message, int index) { return getRepeated(message, index); } + @Override public Object getRepeatedRaw(Builder builder, int index) { return getRepeated(builder, index); } + @Override public void setRepeated(Builder builder, int index, Object value) { getMutableMapField(builder).getMutableList().set(index, (Message) value); } + @Override public void addRepeated(Builder builder, Object value) { getMutableMapField(builder).getMutableList().add((Message) value); } + @Override public boolean has(GeneratedMessage message) { throw new UnsupportedOperationException( "hasField() is not supported for repeated fields."); } + @Override public boolean has(Builder builder) { throw new UnsupportedOperationException( "hasField() is not supported for repeated fields."); } + @Override public int getRepeatedCount(GeneratedMessage message) { return getMapField(message).getList().size(); } + @Override public int getRepeatedCount(Builder builder) { return getMapField(builder).getList().size(); } + @Override public void clear(Builder builder) { getMutableMapField(builder).getMutableList().clear(); } + @Override public com.google.protobuf.Message.Builder newBuilder() { return mapEntryMessageDefaultInstance.newBuilderForType(); } + @Override public com.google.protobuf.Message.Builder getBuilder(Builder builder) { throw new UnsupportedOperationException( "Nested builder not supported for map fields."); } - public com.google.protobuf.Message.Builder getRepeatedBuilder( - Builder builder, int index) { + @Override + public com.google.protobuf.Message.Builder getRepeatedBuilder(Builder builder, int index) { throw new UnsupportedOperationException( "Nested builder not supported for map fields."); } diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java index 81e1862c6..c5adc5ad0 100644 --- a/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java +++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java @@ -30,6 +30,8 @@ package com.google.protobuf; +import com.google.protobuf.AbstractMessageLite.Builder.LimitedInputStream; +import com.google.protobuf.GeneratedMessageLite.EqualsVisitor.NotEqualsException; import com.google.protobuf.Internal.BooleanList; import com.google.protobuf.Internal.DoubleList; import com.google.protobuf.Internal.FloatList; @@ -39,6 +41,7 @@ import com.google.protobuf.Internal.ProtobufList; import com.google.protobuf.WireFormat.FieldType; import java.io.IOException; +import java.io.InputStream; import java.io.ObjectStreamException; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; @@ -57,32 +60,108 @@ import java.util.Map; public abstract class GeneratedMessageLite< MessageType extends GeneratedMessageLite, BuilderType extends GeneratedMessageLite.Builder> - extends AbstractMessageLite - implements Serializable { - - private static final long serialVersionUID = 1L; + extends AbstractMessageLite { /** For use by generated code only. Lazily initialized to reduce allocations. */ - protected UnknownFieldSetLite unknownFields = null; + protected UnknownFieldSetLite unknownFields = UnknownFieldSetLite.getDefaultInstance(); /** For use by generated code only. */ protected int memoizedSerializedSize = -1; + @Override @SuppressWarnings("unchecked") // Guaranteed by runtime. public final Parser getParserForType() { return (Parser) dynamicMethod(MethodToInvoke.GET_PARSER); } + @Override @SuppressWarnings("unchecked") // Guaranteed by runtime. public final MessageType getDefaultInstanceForType() { return (MessageType) dynamicMethod(MethodToInvoke.GET_DEFAULT_INSTANCE); } + @Override @SuppressWarnings("unchecked") // Guaranteed by runtime. public final BuilderType newBuilderForType() { return (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER); } + /** + * A reflective toString function. This is primarily intended as a developer aid, while keeping + * binary size down. The first line of the {@code toString()} representation includes a commented + * version of {@code super.toString()} to act as an indicator that this should not be relied on + * for comparisons. + *

+ * NOTE: This method relies on the field getter methods not being stripped or renamed by proguard. + * If they are, the fields will not be included in the returned string representation. + *

+ * NOTE: This implementation is liable to change in the future, and should not be relied on in + * code. + */ + @Override + public String toString() { + return MessageLiteToString.toString(this, super.toString()); + } + + @SuppressWarnings("unchecked") // Guaranteed by runtime + @Override + public int hashCode() { + if (memoizedHashCode == 0) { + HashCodeVisitor visitor = new HashCodeVisitor(); + visit(visitor, (MessageType) this); + memoizedHashCode = visitor.hashCode; + } + return memoizedHashCode; + } + + @SuppressWarnings("unchecked") // Guaranteed by runtime + int hashCode(HashCodeVisitor visitor) { + if (memoizedHashCode == 0) { + int inProgressHashCode = visitor.hashCode; + visitor.hashCode = 0; + visit(visitor, (MessageType) this); + memoizedHashCode = visitor.hashCode; + visitor.hashCode = inProgressHashCode; + } + return memoizedHashCode; + } + + @SuppressWarnings("unchecked") // Guaranteed by isInstance + runtime + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (!getDefaultInstanceForType().getClass().isInstance(other)) { + return false; + } + + try { + visit(EqualsVisitor.INSTANCE, (MessageType) other); + } catch (NotEqualsException e) { + return false; + } + return true; + } + + /** + * Same as {@link #equals(Object)} but throws {@code NotEqualsException}. + */ + @SuppressWarnings("unchecked") // Guaranteed by isInstance + runtime + boolean equals(EqualsVisitor visitor, MessageLite other) { + if (this == other) { + return true; + } + + if (!getDefaultInstanceForType().getClass().isInstance(other)) { + return false; + } + + visit(visitor, (MessageType) other); + return true; + } + // The general strategy for unknown fields is to use an UnknownFieldSetLite that is treated as // mutable during the parsing constructor and immutable after. This allows us to avoid // any unnecessary intermediary allocations while reducing the generated code size. @@ -91,7 +170,7 @@ public abstract class GeneratedMessageLite< * Lazily initializes unknown fields. */ private final void ensureUnknownFieldsInitialized() { - if (unknownFields == null) { + if (unknownFields == UnknownFieldSetLite.getDefaultInstance()) { unknownFields = UnknownFieldSetLite.newInstance(); } } @@ -130,18 +209,18 @@ public abstract class GeneratedMessageLite< /** * Called by subclasses to complete parsing. For use by generated code only. */ - protected void doneParsing() { - if (unknownFields == null) { - unknownFields = UnknownFieldSetLite.getDefaultInstance(); - } else { - unknownFields.makeImmutable(); - } + protected void makeImmutable() { + dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE); + + unknownFields.makeImmutable(); } + @Override public final boolean isInitialized() { return dynamicMethod(MethodToInvoke.IS_INITIALIZED, Boolean.TRUE) != null; } + @Override public final BuilderType toBuilder() { BuilderType builder = (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER); builder.mergeFrom((MessageType) this); @@ -155,11 +234,14 @@ public abstract class GeneratedMessageLite< * For use by generated code only. */ public static enum MethodToInvoke { + // Rely on/modify instance state IS_INITIALIZED, - PARSE_PARTIAL_FROM, - MERGE_FROM, + VISIT, + MERGE_FROM_STREAM, MAKE_IMMUTABLE, - NEW_INSTANCE, + + // Rely on static state + NEW_MUTABLE_INSTANCE, NEW_BUILDER, GET_DEFAULT_INSTANCE, GET_PARSER; @@ -171,17 +253,18 @@ public abstract class GeneratedMessageLite< * builders in the runtime. This method bundles those operations to reduce the generated methods * count. *

    - *
  • {@code PARSE_PARTIAL_FROM} is parameterized with an {@link CodedInputStream} and + *
  • {@code MERGE_FROM_STREAM} is parameterized with an {@link CodedInputStream} and * {@link ExtensionRegistryLite}. It consumes the input stream, parsing the contents into the * returned protocol buffer. If parsing throws an {@link InvalidProtocolBufferException}, the - * implementation wraps it in a RuntimeException - *
  • {@code NEW_INSTANCE} returns a new instance of the protocol buffer + * implementation wraps it in a RuntimeException. + *
  • {@code NEW_INSTANCE} returns a new instance of the protocol buffer that has not yet been + * made immutable. See {@code MAKE_IMMUTABLE}. *
  • {@code IS_INITIALIZED} is parameterized with a {@code Boolean} detailing whether to * memoize. It returns {@code null} for false and the default instance for true. We optionally * memoize to support the Builder case, where memoization is not desired. *
  • {@code NEW_BUILDER} returns a {@code BuilderType} instance. - *
  • {@code MERGE_FROM} is parameterized with a {@code MessageType} and merges the fields from - * that instance into this instance. + *
  • {@code VISIT} is parameterized with a {@code Visitor} and a {@code MessageType} and + * recursively iterates through the fields side by side between this and the instance. *
  • {@code MAKE_IMMUTABLE} sets all internal fields to an immutable state. *
* This method, plus the implementation of the Builder, enables the Builder class to be proguarded @@ -205,6 +288,11 @@ public abstract class GeneratedMessageLite< return dynamicMethod(method, null, null); } + void visit(Visitor visitor, MessageType other) { + dynamicMethod(MethodToInvoke.VISIT, visitor, other); + unknownFields = visitor.visitUnknownFields(unknownFields, other.unknownFields); + } + /** * Merge some unknown fields into the {@link UnknownFieldSetLite} for this * message. @@ -219,7 +307,7 @@ public abstract class GeneratedMessageLite< public abstract static class Builder< MessageType extends GeneratedMessageLite, BuilderType extends Builder> - extends AbstractMessageLite.Builder { + extends AbstractMessageLite.Builder { private final MessageType defaultInstance; protected MessageType instance; @@ -227,7 +315,8 @@ public abstract class GeneratedMessageLite< protected Builder(MessageType defaultInstance) { this.defaultInstance = defaultInstance; - this.instance = (MessageType) defaultInstance.dynamicMethod(MethodToInvoke.NEW_INSTANCE); + this.instance = + (MessageType) defaultInstance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); isBuilt = false; } @@ -237,26 +326,27 @@ public abstract class GeneratedMessageLite< */ protected void copyOnWrite() { if (isBuilt) { - MessageType newInstance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_INSTANCE); - newInstance.dynamicMethod(MethodToInvoke.MERGE_FROM, instance); + MessageType newInstance = + (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); + newInstance.visit(MergeFromVisitor.INSTANCE, instance); instance = newInstance; isBuilt = false; } } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public final boolean isInitialized() { return GeneratedMessageLite.isInitialized(instance, false /* shouldMemoize */); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public final BuilderType clear() { // No need to copy on write since we're dropping the instance anyways. - instance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_INSTANCE); + instance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); return (BuilderType) this; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public BuilderType clone() { BuilderType builder = (BuilderType) getDefaultInstanceForType().newBuilderForType(); @@ -264,20 +354,19 @@ public abstract class GeneratedMessageLite< return builder; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public MessageType buildPartial() { if (isBuilt) { return instance; } - instance.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE); - instance.unknownFields.makeImmutable(); + instance.makeImmutable(); isBuilt = true; return instance; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public final MessageType build() { MessageType result = buildPartial(); if (!result.isInitialized()) { @@ -286,33 +375,36 @@ public abstract class GeneratedMessageLite< return result; } + @Override + protected BuilderType internalMergeFrom(MessageType message) { + return mergeFrom(message); + } + /** All subclasses implement this. */ public BuilderType mergeFrom(MessageType message) { copyOnWrite(); - instance.dynamicMethod(MethodToInvoke.MERGE_FROM, message); + instance.visit(MergeFromVisitor.INSTANCE, message); return (BuilderType) this; } + @Override public MessageType getDefaultInstanceForType() { return defaultInstance; } + @Override public BuilderType mergeFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - MessageType parsedMessage = null; + throws IOException { + copyOnWrite(); try { - parsedMessage = - (MessageType) getDefaultInstanceForType().getParserForType().parsePartialFrom( - input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (MessageType) e.getUnfinishedMessage(); - throw e; - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); + instance.dynamicMethod(MethodToInvoke.MERGE_FROM_STREAM, input, extensionRegistry); + } catch (RuntimeException e) { + if (e.getCause() instanceof IOException) { + throw (IOException) e.getCause(); } + throw e; } return (BuilderType) this; } @@ -368,6 +460,12 @@ public abstract class GeneratedMessageLite< } extensions.mergeFrom(((ExtendableMessage) other).extensions); } + + @Override + final void visit(Visitor visitor, MessageType other) { + super.visit(visitor, other); + extensions = visitor.visitExtensions(extensions, other.extensions); + } /** * Parse an unknown field or an extension. For use by generated code only. @@ -505,9 +603,8 @@ public abstract class GeneratedMessageLite< } /** Check if a singular extension is present. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) - public final boolean hasExtension( - final ExtensionLite extension) { + @Override + public final boolean hasExtension(final ExtensionLite extension) { GeneratedExtension extensionLite = checkIsLite(extension); @@ -516,7 +613,7 @@ public abstract class GeneratedMessageLite< } /** Get the number of elements in a repeated extension. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public final int getExtensionCount( final ExtensionLite> extension) { GeneratedExtension> extensionLite = @@ -527,10 +624,9 @@ public abstract class GeneratedMessageLite< } /** Get the value of an extension. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override @SuppressWarnings("unchecked") - public final Type getExtension( - final ExtensionLite extension) { + public final Type getExtension(final ExtensionLite extension) { GeneratedExtension extensionLite = checkIsLite(extension); @@ -544,11 +640,10 @@ public abstract class GeneratedMessageLite< } /** Get one element of a repeated extension. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override @SuppressWarnings("unchecked") public final Type getExtension( - final ExtensionLite> extension, - final int index) { + final ExtensionLite> extension, final int index) { GeneratedExtension> extensionLite = checkIsLite(extension); @@ -562,10 +657,9 @@ public abstract class GeneratedMessageLite< return extensions.isInitialized(); } - @Override - protected final void doneParsing() { - super.doneParsing(); + protected final void makeImmutable() { + super.makeImmutable(); extensions.makeImmutable(); } @@ -654,7 +748,7 @@ public abstract class GeneratedMessageLite< instance.extensions = extensions; } - // @Override (Java 1.6 override semantics, but we must support 1.5) + @Override protected void copyOnWrite() { if (!isBuilt) { return; @@ -664,7 +758,7 @@ public abstract class GeneratedMessageLite< instance.extensions = instance.extensions.clone(); } - // @Override (Java 1.6 override semantics, but we must support 1.5) + @Override public final MessageType buildPartial() { if (isBuilt) { return instance; @@ -686,33 +780,30 @@ public abstract class GeneratedMessageLite< } /** Check if a singular extension is present. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) - public final boolean hasExtension( - final ExtensionLite extension) { + @Override + public final boolean hasExtension(final ExtensionLite extension) { return instance.hasExtension(extension); } /** Get the number of elements in a repeated extension. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public final int getExtensionCount( final ExtensionLite> extension) { return instance.getExtensionCount(extension); } /** Get the value of an extension. */ - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override @SuppressWarnings("unchecked") - public final Type getExtension( - final ExtensionLite extension) { + public final Type getExtension(final ExtensionLite extension) { return instance.getExtension(extension); } /** Get one element of a repeated extension. */ + @Override @SuppressWarnings("unchecked") - //@Override (Java 1.6 override semantics, but we must support 1.5) public final Type getExtension( - final ExtensionLite> extension, - final int index) { + final ExtensionLite> extension, final int index) { return instance.getExtension(extension, index); } @@ -844,37 +935,44 @@ public abstract class GeneratedMessageLite< final boolean isRepeated; final boolean isPacked; + @Override public int getNumber() { return number; } + @Override public WireFormat.FieldType getLiteType() { return type; } + @Override public WireFormat.JavaType getLiteJavaType() { return type.getJavaType(); } + @Override public boolean isRepeated() { return isRepeated; } + @Override public boolean isPacked() { return isPacked; } + @Override public Internal.EnumLiteMap getEnumType() { return enumTypeMap; } + @Override @SuppressWarnings("unchecked") - public MessageLite.Builder internalMergeFrom( - MessageLite.Builder to, MessageLite from) { + public MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from) { return ((Builder) to).mergeFrom((GeneratedMessageLite) from); } + @Override public int compareTo(ExtensionDescriptor other) { return number - other.number; } @@ -969,6 +1067,7 @@ public abstract class GeneratedMessageLite< } /** Get the field number. */ + @Override public int getNumber() { return descriptor.getNumber(); } @@ -978,6 +1077,7 @@ public abstract class GeneratedMessageLite< * If the extension is an embedded message or group, returns the default * instance of the message. */ + @Override public MessageLite getMessageDefaultInstance() { return messageDefaultInstance; } @@ -1032,14 +1132,17 @@ public abstract class GeneratedMessageLite< } } + @Override public FieldType getLiteType() { return descriptor.getLiteType(); } + @Override public boolean isRepeated() { return descriptor.isRepeated; } + @Override public Type getDefaultValue() { return defaultValue; } @@ -1049,7 +1152,12 @@ public abstract class GeneratedMessageLite< * A serialized (serializable) form of the generated message. Stores the * message as a class name and a byte array. */ - static final class SerializedForm implements Serializable { + protected static final class SerializedForm implements Serializable { + + public static SerializedForm of(MessageLite message) { + return new SerializedForm(message); + } + private static final long serialVersionUID = 0L; private final String messageClassName; @@ -1093,16 +1201,6 @@ public abstract class GeneratedMessageLite< } } } - - /** - * Replaces this object in the output stream with a serialized form. - * Part of Java's serialization magic. Generated sub-classes must override - * this method by calling {@code return super.writeReplace();} - * @return a SerializedForm of this message - */ - protected Object writeReplace() throws ObjectStreamException { - return new SerializedForm(this); - } /** * Checks that the {@link Extension} is Lite and returns it as a @@ -1129,30 +1227,72 @@ public abstract class GeneratedMessageLite< protected static final > boolean isInitialized( T message, boolean shouldMemoize) { return message.dynamicMethod(MethodToInvoke.IS_INITIALIZED, shouldMemoize) != null; - } + } protected static final > void makeImmutable(T message) { message.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE); } - - /** - * A static helper method for parsing a partial from input using the extension registry and the - * instance. - */ - static > T parsePartialFrom( - T instance, CodedInputStream input, ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - try { - return (T) instance.dynamicMethod( - MethodToInvoke.PARSE_PARTIAL_FROM, input, extensionRegistry); - } catch (RuntimeException e) { - if (e.getCause() instanceof InvalidProtocolBufferException) { - throw (InvalidProtocolBufferException) e.getCause(); - } - throw e; - } + + protected static IntList emptyIntList() { + return IntArrayList.emptyList(); + } + + protected static IntList mutableCopy(IntList list) { + int size = list.size(); + return list.mutableCopyWithCapacity( + size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2); + } + + protected static LongList emptyLongList() { + return LongArrayList.emptyList(); } + protected static LongList mutableCopy(LongList list) { + int size = list.size(); + return list.mutableCopyWithCapacity( + size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2); + } + + protected static FloatList emptyFloatList() { + return FloatArrayList.emptyList(); + } + + protected static FloatList mutableCopy(FloatList list) { + int size = list.size(); + return list.mutableCopyWithCapacity( + size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2); + } + + protected static DoubleList emptyDoubleList() { + return DoubleArrayList.emptyList(); + } + + protected static DoubleList mutableCopy(DoubleList list) { + int size = list.size(); + return list.mutableCopyWithCapacity( + size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2); + } + + protected static BooleanList emptyBooleanList() { + return BooleanArrayList.emptyList(); + } + + protected static BooleanList mutableCopy(BooleanList list) { + int size = list.size(); + return list.mutableCopyWithCapacity( + size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2); + } + + protected static ProtobufList emptyProtobufList() { + return ProtobufArrayList.emptyList(); + } + + protected static ProtobufList mutableCopy(ProtobufList list) { + int size = list.size(); + return list.mutableCopyWithCapacity( + size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2); + } + /** * A {@link Parser} implementation that delegates to the default instance. *

@@ -1174,103 +1314,929 @@ public abstract class GeneratedMessageLite< } } - protected static IntList newIntList() { - return new IntArrayList(); + /** + * A static helper method for parsing a partial from input using the extension registry and the + * instance. + */ + // TODO(dweis): Should this verify that the last tag was 0? + static > T parsePartialFrom( + T instance, CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + @SuppressWarnings("unchecked") // Guaranteed by protoc + T result = (T) instance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); + try { + result.dynamicMethod(MethodToInvoke.MERGE_FROM_STREAM, input, extensionRegistry); + result.makeImmutable(); + } catch (RuntimeException e) { + if (e.getCause() instanceof InvalidProtocolBufferException) { + throw (InvalidProtocolBufferException) e.getCause(); + } + throw e; + } + return result; } - protected static IntList newIntListWithCapacity(int capacity) { - return new IntArrayList(capacity); + protected static > T parsePartialFrom( + T defaultInstance, + CodedInputStream input) + throws InvalidProtocolBufferException { + return parsePartialFrom(defaultInstance, input, ExtensionRegistryLite.getEmptyRegistry()); } - protected static IntList newIntList(List toCopy) { - return new IntArrayList(toCopy); - } - - protected static IntList emptyIntList() { - return IntArrayList.emptyList(); + /** + * Helper method to check if message is initialized. + * + * @throws InvalidProtocolBufferException if it is not initialized. + * @return The message to check. + */ + private static > T checkMessageInitialized(T message) + throws InvalidProtocolBufferException { + if (message != null && !message.isInitialized()) { + throw message.newUninitializedMessageException() + .asInvalidProtocolBufferException() + .setUnfinishedMessage(message); + } + return message; } - protected static LongList newLongList() { - return new LongArrayList(); + // Validates last tag. + protected static > T parseFrom( + T defaultInstance, ByteString data) + throws InvalidProtocolBufferException { + return checkMessageInitialized( + parseFrom(defaultInstance, data, ExtensionRegistryLite.getEmptyRegistry())); } - protected static LongList newLongListWithCapacity(int capacity) { - return new LongArrayList(capacity); + // Validates last tag. + protected static > T parseFrom( + T defaultInstance, ByteString data, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + return checkMessageInitialized(parsePartialFrom(defaultInstance, data, extensionRegistry)); } - protected static LongList newLongList(List toCopy) { - return new LongArrayList(toCopy); + // This is a special case since we want to verify that the last tag is 0. We assume we exhaust the + // ByteString. + private static > T parsePartialFrom( + T defaultInstance, ByteString data, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + T message; + try { + CodedInputStream input = data.newCodedInput(); + message = parsePartialFrom(defaultInstance, input, extensionRegistry); + try { + input.checkLastTagWas(0); + } catch (InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(message); + } + return message; + } catch (InvalidProtocolBufferException e) { + throw e; + } } - protected static LongList emptyLongList() { - return LongArrayList.emptyList(); + // This is a special case since we want to verify that the last tag is 0. We assume we exhaust the + // ByteString. + private static > T parsePartialFrom( + T defaultInstance, byte[] data, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + T message; + try { + CodedInputStream input = CodedInputStream.newInstance(data); + message = parsePartialFrom(defaultInstance, input, extensionRegistry); + try { + input.checkLastTagWas(0); + } catch (InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(message); + } + return message; + } catch (InvalidProtocolBufferException e) { + throw e; + } + } + + // Validates last tag. + protected static > T parseFrom( + T defaultInstance, byte[] data) + throws InvalidProtocolBufferException { + return checkMessageInitialized( + parsePartialFrom(defaultInstance, data, ExtensionRegistryLite.getEmptyRegistry())); + } + + // Validates last tag. + protected static > T parseFrom( + T defaultInstance, byte[] data, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + return checkMessageInitialized(parsePartialFrom(defaultInstance, data, extensionRegistry)); + } + + // Does not validate last tag. + protected static > T parseFrom( + T defaultInstance, InputStream input) + throws InvalidProtocolBufferException { + return checkMessageInitialized( + parsePartialFrom(defaultInstance, CodedInputStream.newInstance(input), + ExtensionRegistryLite.getEmptyRegistry())); + } + + // Does not validate last tag. + protected static > T parseFrom( + T defaultInstance, InputStream input, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + return checkMessageInitialized( + parsePartialFrom(defaultInstance, CodedInputStream.newInstance(input), extensionRegistry)); + } + + // Does not validate last tag. + protected static > T parseFrom( + T defaultInstance, CodedInputStream input) + throws InvalidProtocolBufferException { + return parseFrom(defaultInstance, input, ExtensionRegistryLite.getEmptyRegistry()); + } + + // Does not validate last tag. + protected static > T parseFrom( + T defaultInstance, CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + return checkMessageInitialized( + parsePartialFrom(defaultInstance, input, extensionRegistry)); + } + + // Validates last tag. + protected static > T parseDelimitedFrom( + T defaultInstance, InputStream input) + throws InvalidProtocolBufferException { + return checkMessageInitialized( + parsePartialDelimitedFrom(defaultInstance, input, + ExtensionRegistryLite.getEmptyRegistry())); + } + + // Validates last tag. + protected static > T parseDelimitedFrom( + T defaultInstance, InputStream input, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + return checkMessageInitialized( + parsePartialDelimitedFrom(defaultInstance, input, extensionRegistry)); } - protected static FloatList newFloatList() { - return new FloatArrayList(); + private static > T parsePartialDelimitedFrom( + T defaultInstance, + InputStream input, + ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + int size; + try { + int firstByte = input.read(); + if (firstByte == -1) { + return null; + } + size = CodedInputStream.readRawVarint32(firstByte, input); + } catch (IOException e) { + throw new InvalidProtocolBufferException(e.getMessage()); + } + InputStream limitedInput = new LimitedInputStream(input, size); + CodedInputStream codedInput = CodedInputStream.newInstance(limitedInput); + T message = parsePartialFrom(defaultInstance, codedInput, extensionRegistry); + try { + codedInput.checkLastTagWas(0); + } catch (InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(message); + } + return message; } - - protected static FloatList newFloatListWithCapacity(int capacity) { - return new FloatArrayList(capacity); + + /** + * An abstract visitor that the generated code calls into that we use to implement various + * features. Fields that are not members of oneofs are always visited. Members of a oneof are only + * visited when they are the set oneof case value on the "other" proto. The visitOneofNotSet + * method is invoked if other's oneof case is not set. + */ + protected interface Visitor { + boolean visitBoolean(boolean minePresent, boolean mine, boolean otherPresent, boolean other); + int visitInt(boolean minePresent, int mine, boolean otherPresent, int other); + double visitDouble(boolean minePresent, double mine, boolean otherPresent, double other); + float visitFloat(boolean minePresent, float mine, boolean otherPresent, float other); + long visitLong(boolean minePresent, long mine, boolean otherPresent, long other); + String visitString(boolean minePresent, String mine, boolean otherPresent, String other); + ByteString visitByteString( + boolean minePresent, ByteString mine, boolean otherPresent, ByteString other); + + Object visitOneofBoolean(boolean minePresent, Object mine, Object other); + Object visitOneofInt(boolean minePresent, Object mine, Object other); + Object visitOneofDouble(boolean minePresent, Object mine, Object other); + Object visitOneofFloat(boolean minePresent, Object mine, Object other); + Object visitOneofLong(boolean minePresent, Object mine, Object other); + Object visitOneofString(boolean minePresent, Object mine, Object other); + Object visitOneofByteString(boolean minePresent, Object mine, Object other); + Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other); + Object visitOneofMessage(boolean minePresent, Object mine, Object other); + void visitOneofNotSet(boolean minePresent); + + /** + * Message fields use null sentinals. + */ + T visitMessage(T mine, T other); + LazyFieldLite visitLazyMessage( + boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other); + + ProtobufList visitList(ProtobufList mine, ProtobufList other); + BooleanList visitBooleanList(BooleanList mine, BooleanList other); + IntList visitIntList(IntList mine, IntList other); + DoubleList visitDoubleList(DoubleList mine, DoubleList other); + FloatList visitFloatList(FloatList mine, FloatList other); + LongList visitLongList(LongList mine, LongList other); + FieldSet visitExtensions( + FieldSet mine, FieldSet other); + UnknownFieldSetLite visitUnknownFields(UnknownFieldSetLite mine, UnknownFieldSetLite other); + MapFieldLite visitMap(MapFieldLite mine, MapFieldLite other); } - - protected static FloatList newFloatList(List toCopy) { - return new FloatArrayList(toCopy); + + /** + * Implements equals. Throws a {@link NotEqualsException} when not equal. + */ + static class EqualsVisitor implements Visitor { + + static final class NotEqualsException extends RuntimeException {} + + static final EqualsVisitor INSTANCE = new EqualsVisitor(); + + static final NotEqualsException NOT_EQUALS = new NotEqualsException(); + + private EqualsVisitor() {} + + @Override + public boolean visitBoolean( + boolean minePresent, boolean mine, boolean otherPresent, boolean other) { + if (minePresent != otherPresent || mine != other) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public int visitInt(boolean minePresent, int mine, boolean otherPresent, int other) { + if (minePresent != otherPresent || mine != other) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public double visitDouble( + boolean minePresent, double mine, boolean otherPresent, double other) { + if (minePresent != otherPresent || mine != other) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public float visitFloat(boolean minePresent, float mine, boolean otherPresent, float other) { + if (minePresent != otherPresent || mine != other) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public long visitLong(boolean minePresent, long mine, boolean otherPresent, long other) { + if (minePresent != otherPresent || mine != other) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public String visitString( + boolean minePresent, String mine, boolean otherPresent, String other) { + if (minePresent != otherPresent || !mine.equals(other)) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public ByteString visitByteString( + boolean minePresent, ByteString mine, boolean otherPresent, ByteString other) { + if (minePresent != otherPresent || !mine.equals(other)) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public Object visitOneofBoolean(boolean minePresent, Object mine, Object other) { + if (minePresent && mine.equals(other)) { + return mine; + } + throw NOT_EQUALS; + } + + @Override + public Object visitOneofInt(boolean minePresent, Object mine, Object other) { + if (minePresent && mine.equals(other)) { + return mine; + } + throw NOT_EQUALS; + } + + @Override + public Object visitOneofDouble(boolean minePresent, Object mine, Object other) { + if (minePresent && mine.equals(other)) { + return mine; + } + throw NOT_EQUALS; + } + + @Override + public Object visitOneofFloat(boolean minePresent, Object mine, Object other) { + if (minePresent && mine.equals(other)) { + return mine; + } + throw NOT_EQUALS; + } + + @Override + public Object visitOneofLong(boolean minePresent, Object mine, Object other) { + if (minePresent && mine.equals(other)) { + return mine; + } + throw NOT_EQUALS; + } + + @Override + public Object visitOneofString(boolean minePresent, Object mine, Object other) { + if (minePresent && mine.equals(other)) { + return mine; + } + throw NOT_EQUALS; + } + + @Override + public Object visitOneofByteString(boolean minePresent, Object mine, Object other) { + if (minePresent && mine.equals(other)) { + return mine; + } + throw NOT_EQUALS; + } + + @Override + public Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other) { + if (minePresent && mine.equals(other)) { + return mine; + } + throw NOT_EQUALS; + } + + @Override + public Object visitOneofMessage(boolean minePresent, Object mine, Object other) { + if (minePresent && ((GeneratedMessageLite) mine).equals(this, (MessageLite) other)) { + return mine; + } + throw NOT_EQUALS; + } + + @Override + public void visitOneofNotSet(boolean minePresent) { + if (minePresent) { + throw NOT_EQUALS; + } + } + + @Override + public T visitMessage(T mine, T other) { + if (mine == null && other == null) { + return null; + } + + if (mine == null || other == null) { + throw NOT_EQUALS; + } + + ((GeneratedMessageLite) mine).equals(this, other); + + return mine; + } + + @Override + public LazyFieldLite visitLazyMessage( + boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other) { + if (!minePresent && !otherPresent) { + return mine; + } else if (minePresent && otherPresent && mine.equals(other)) { + return mine; + } + throw NOT_EQUALS; + } + + @Override + public ProtobufList visitList(ProtobufList mine, ProtobufList other) { + if (!mine.equals(other)) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public BooleanList visitBooleanList(BooleanList mine, BooleanList other) { + if (!mine.equals(other)) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public IntList visitIntList(IntList mine, IntList other) { + if (!mine.equals(other)) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public DoubleList visitDoubleList(DoubleList mine, DoubleList other) { + if (!mine.equals(other)) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public FloatList visitFloatList(FloatList mine, FloatList other) { + if (!mine.equals(other)) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public LongList visitLongList(LongList mine, LongList other) { + if (!mine.equals(other)) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public FieldSet visitExtensions( + FieldSet mine, + FieldSet other) { + if (!mine.equals(other)) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public UnknownFieldSetLite visitUnknownFields( + UnknownFieldSetLite mine, + UnknownFieldSetLite other) { + if (!mine.equals(other)) { + throw NOT_EQUALS; + } + return mine; + } + + @Override + public MapFieldLite visitMap(MapFieldLite mine, MapFieldLite other) { + if (!mine.equals(other)) { + throw NOT_EQUALS; + } + return mine; + } } - - protected static FloatList emptyFloatList() { - return FloatArrayList.emptyList(); + + /** + * Implements hashCode by accumulating state. + */ + private static class HashCodeVisitor implements Visitor { + + // The caller must ensure that the visitor is invoked parameterized with this and this such that + // other is this. This is required due to how oneof cases are handled. See the class comment + // on Visitor for more information. + + private int hashCode = 0; + + @Override + public boolean visitBoolean( + boolean minePresent, boolean mine, boolean otherPresent, boolean other) { + hashCode = (53 * hashCode) + Internal.hashBoolean(mine); + return mine; + } + + @Override + public int visitInt(boolean minePresent, int mine, boolean otherPresent, int other) { + hashCode = (53 * hashCode) + mine; + return mine; + } + + @Override + public double visitDouble( + boolean minePresent, double mine, boolean otherPresent, double other) { + hashCode = (53 * hashCode) + Internal.hashLong(Double.doubleToLongBits(mine)); + return mine; + } + + @Override + public float visitFloat(boolean minePresent, float mine, boolean otherPresent, float other) { + hashCode = (53 * hashCode) + Float.floatToIntBits(mine); + return mine; + } + + @Override + public long visitLong(boolean minePresent, long mine, boolean otherPresent, long other) { + hashCode = (53 * hashCode) + Internal.hashLong(mine); + return mine; + } + + @Override + public String visitString( + boolean minePresent, String mine, boolean otherPresent, String other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public ByteString visitByteString( + boolean minePresent, ByteString mine, boolean otherPresent, ByteString other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public Object visitOneofBoolean(boolean minePresent, Object mine, Object other) { + hashCode = (53 * hashCode) + Internal.hashBoolean(((Boolean) mine)); + return mine; + } + + @Override + public Object visitOneofInt(boolean minePresent, Object mine, Object other) { + hashCode = (53 * hashCode) + (Integer) mine; + return mine; + } + + @Override + public Object visitOneofDouble(boolean minePresent, Object mine, Object other) { + hashCode = (53 * hashCode) + Internal.hashLong(Double.doubleToLongBits((Double) mine)); + return mine; + } + + @Override + public Object visitOneofFloat(boolean minePresent, Object mine, Object other) { + hashCode = (53 * hashCode) + Float.floatToIntBits((Float) mine); + return mine; + } + + @Override + public Object visitOneofLong(boolean minePresent, Object mine, Object other) { + hashCode = (53 * hashCode) + Internal.hashLong((Long) mine); + return mine; + } + + @Override + public Object visitOneofString(boolean minePresent, Object mine, Object other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public Object visitOneofByteString(boolean minePresent, Object mine, Object other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public Object visitOneofMessage(boolean minePresent, Object mine, Object other) { + return visitMessage((MessageLite) mine, (MessageLite) other); + } + + @Override + public void visitOneofNotSet(boolean minePresent) { + if (minePresent) { + throw new IllegalStateException(); // Can't happen if other == this. + } + } + + @Override + public T visitMessage(T mine, T other) { + final int protoHash; + if (mine != null) { + if (mine instanceof GeneratedMessageLite) { + protoHash = ((GeneratedMessageLite) mine).hashCode(this); + } else { + protoHash = mine.hashCode(); + } + } else { + protoHash = 37; + } + hashCode = (53 * hashCode) + protoHash; + return mine; + } + + @Override + public LazyFieldLite visitLazyMessage( + boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public ProtobufList visitList(ProtobufList mine, ProtobufList other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public BooleanList visitBooleanList(BooleanList mine, BooleanList other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public IntList visitIntList(IntList mine, IntList other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public DoubleList visitDoubleList(DoubleList mine, DoubleList other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public FloatList visitFloatList(FloatList mine, FloatList other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public LongList visitLongList(LongList mine, LongList other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public FieldSet visitExtensions( + FieldSet mine, + FieldSet other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public UnknownFieldSetLite visitUnknownFields( + UnknownFieldSetLite mine, + UnknownFieldSetLite other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } + + @Override + public MapFieldLite visitMap(MapFieldLite mine, MapFieldLite other) { + hashCode = (53 * hashCode) + mine.hashCode(); + return mine; + } } - - protected static DoubleList newDoubleList() { - return new DoubleArrayList(); - } - - protected static DoubleList newDoubleListWithCapacity(int capacity) { - return new DoubleArrayList(capacity); - } - - protected static DoubleList newDoubleList(List toCopy) { - return new DoubleArrayList(toCopy); - } - - protected static DoubleList emptyDoubleList() { - return DoubleArrayList.emptyList(); - } - - protected static BooleanList newBooleanList() { - return new BooleanArrayList(); - } - - protected static BooleanList newBooleanListWithCapacity(int capacity) { - return new BooleanArrayList(capacity); - } - - protected static BooleanList newBooleanList(List toCopy) { - return new BooleanArrayList(toCopy); - } - - protected static BooleanList emptyBooleanList() { - return BooleanArrayList.emptyList(); - } - - protected static ProtobufList newProtobufList() { - return new ProtobufArrayList(); - } - - protected static ProtobufList newProtobufList(List toCopy) { - return new ProtobufArrayList(toCopy); - } - - protected static ProtobufList newProtobufListWithCapacity(int capacity) { - return new ProtobufArrayList(capacity); - } - - protected static ProtobufList emptyProtobufList() { - return ProtobufArrayList.emptyList(); - } - - protected static LazyStringArrayList emptyLazyStringArrayList() { - return LazyStringArrayList.emptyList(); + + /** + * Implements field merging semantics over the visitor interface. + */ + protected static class MergeFromVisitor implements Visitor { + + public static final MergeFromVisitor INSTANCE = new MergeFromVisitor(); + + private MergeFromVisitor() {} + + @Override + public boolean visitBoolean( + boolean minePresent, boolean mine, boolean otherPresent, boolean other) { + return otherPresent ? other : mine; + } + + @Override + public int visitInt(boolean minePresent, int mine, boolean otherPresent, int other) { + return otherPresent ? other : mine; + } + + @Override + public double visitDouble( + boolean minePresent, double mine, boolean otherPresent, double other) { + return otherPresent ? other : mine; + } + + @Override + public float visitFloat(boolean minePresent, float mine, boolean otherPresent, float other) { + return otherPresent ? other : mine; + } + + @Override + public long visitLong(boolean minePresent, long mine, boolean otherPresent, long other) { + return otherPresent ? other : mine; + } + + @Override + public String visitString( + boolean minePresent, String mine, boolean otherPresent, String other) { + return otherPresent ? other : mine; + } + + @Override + public ByteString visitByteString( + boolean minePresent, ByteString mine, boolean otherPresent, ByteString other) { + return otherPresent ? other : mine; + } + + @Override + public Object visitOneofBoolean(boolean minePresent, Object mine, Object other) { + return other; + } + + @Override + public Object visitOneofInt(boolean minePresent, Object mine, Object other) { + return other; + } + + @Override + public Object visitOneofDouble(boolean minePresent, Object mine, Object other) { + return other; + } + + @Override + public Object visitOneofFloat(boolean minePresent, Object mine, Object other) { + return other; + } + + @Override + public Object visitOneofLong(boolean minePresent, Object mine, Object other) { + return other; + } + + @Override + public Object visitOneofString(boolean minePresent, Object mine, Object other) { + return other; + } + + @Override + public Object visitOneofByteString(boolean minePresent, Object mine, Object other) { + return other; + } + + @Override + public Object visitOneofLazyMessage(boolean minePresent, Object mine, Object other) { + if (minePresent) { + LazyFieldLite lazy = (LazyFieldLite) mine; + lazy.merge((LazyFieldLite) other); + return lazy; + } + return other; + } + + @Override + public Object visitOneofMessage(boolean minePresent, Object mine, Object other) { + if (minePresent) { + return visitMessage((MessageLite) mine, (MessageLite) other); + } + return other; + } + + @Override + public void visitOneofNotSet(boolean minePresent) { + return; + } + + @SuppressWarnings("unchecked") // Guaranteed by runtime. + @Override + public T visitMessage(T mine, T other) { + if (mine != null && other != null) { + return (T) mine.toBuilder().mergeFrom(other).build(); + } + + return mine != null ? mine : other; + } + + @Override + public LazyFieldLite visitLazyMessage( + boolean minePresent, LazyFieldLite mine, boolean otherPresent, LazyFieldLite other) { + // LazyFieldLite's are never null so we can just copy across. Necessary to avoid leakage + // from builder into immutable message. + // TODO(dweis): Change to null sentinels? + mine.merge(other); + return mine; + } + + @Override + public ProtobufList visitList(ProtobufList mine, ProtobufList other) { + int size = mine.size(); + int otherSize = other.size(); + if (size > 0 && otherSize > 0) { + if (!mine.isModifiable()) { + mine = mine.mutableCopyWithCapacity(size + otherSize); + } + mine.addAll(other); + } + + return size > 0 ? mine : other; + } + + @Override + public BooleanList visitBooleanList(BooleanList mine, BooleanList other) { + int size = mine.size(); + int otherSize = other.size(); + if (size > 0 && otherSize > 0) { + if (!mine.isModifiable()) { + mine = mine.mutableCopyWithCapacity(size + otherSize); + } + mine.addAll(other); + } + + return size > 0 ? mine : other; + } + + @Override + public IntList visitIntList(IntList mine, IntList other) { + int size = mine.size(); + int otherSize = other.size(); + if (size > 0 && otherSize > 0) { + if (!mine.isModifiable()) { + mine = mine.mutableCopyWithCapacity(size + otherSize); + } + mine.addAll(other); + } + + return size > 0 ? mine : other; + } + + @Override + public DoubleList visitDoubleList(DoubleList mine, DoubleList other) { + int size = mine.size(); + int otherSize = other.size(); + if (size > 0 && otherSize > 0) { + if (!mine.isModifiable()) { + mine = mine.mutableCopyWithCapacity(size + otherSize); + } + mine.addAll(other); + } + + return size > 0 ? mine : other; + } + + @Override + public FloatList visitFloatList(FloatList mine, FloatList other) { + int size = mine.size(); + int otherSize = other.size(); + if (size > 0 && otherSize > 0) { + if (!mine.isModifiable()) { + mine = mine.mutableCopyWithCapacity(size + otherSize); + } + mine.addAll(other); + } + + return size > 0 ? mine : other; + } + + @Override + public LongList visitLongList(LongList mine, LongList other) { + int size = mine.size(); + int otherSize = other.size(); + if (size > 0 && otherSize > 0) { + if (!mine.isModifiable()) { + mine = mine.mutableCopyWithCapacity(size + otherSize); + } + mine.addAll(other); + } + + return size > 0 ? mine : other; + } + + @Override + public FieldSet visitExtensions( + FieldSet mine, + FieldSet other) { + if (mine.isImmutable()) { + mine = mine.clone(); + } + mine.mergeFrom(other); + return mine; + } + + @Override + public UnknownFieldSetLite visitUnknownFields( + UnknownFieldSetLite mine, + UnknownFieldSetLite other) { + return other == UnknownFieldSetLite.getDefaultInstance() + ? mine : UnknownFieldSetLite.mutableCopyOf(mine, other); + } + + @Override + public MapFieldLite visitMap(MapFieldLite mine, MapFieldLite other) { + mine.mergeFrom(other); + return mine; + } } } diff --git a/java/core/src/main/java/com/google/protobuf/IntArrayList.java b/java/core/src/main/java/com/google/protobuf/IntArrayList.java index f4e68ed89..6d6ece5a2 100644 --- a/java/core/src/main/java/com/google/protobuf/IntArrayList.java +++ b/java/core/src/main/java/com/google/protobuf/IntArrayList.java @@ -34,7 +34,6 @@ import com.google.protobuf.Internal.IntList; import java.util.Arrays; import java.util.Collection; -import java.util.List; import java.util.RandomAccess; /** @@ -44,8 +43,6 @@ import java.util.RandomAccess; */ final class IntArrayList extends AbstractProtobufList implements IntList, RandomAccess { - private static final int DEFAULT_CAPACITY = 10; - private static final IntArrayList EMPTY_LIST = new IntArrayList(); static { EMPTY_LIST.makeImmutable(); @@ -70,32 +67,55 @@ final class IntArrayList extends AbstractProtobufList implements IntLis * Constructs a new mutable {@code IntArrayList} with default capacity. */ IntArrayList() { - this(DEFAULT_CAPACITY); - } - - /** - * Constructs a new mutable {@code IntArrayList} with the provided capacity. - */ - IntArrayList(int capacity) { - array = new int[capacity]; - size = 0; + this(new int[DEFAULT_CAPACITY], 0); } /** * Constructs a new mutable {@code IntArrayList} containing the same elements as {@code other}. */ - IntArrayList(List other) { - if (other instanceof IntArrayList) { - IntArrayList list = (IntArrayList) other; - array = list.array.clone(); - size = list.size; - } else { - size = other.size(); - array = new int[size]; - for (int i = 0; i < size; i++) { - array[i] = other.get(i); + private IntArrayList(int[] array, int size) { + this.array = array; + this.size = size; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof IntArrayList)) { + return super.equals(o); + } + IntArrayList other = (IntArrayList) o; + if (size != other.size) { + return false; + } + + final int[] arr = other.array; + for (int i = 0; i < size; i++) { + if (array[i] != arr[i]) { + return false; } } + + return true; + } + + @Override + public int hashCode() { + int result = 1; + for (int i = 0; i < size; i++) { + result = (31 * result) + array[i]; + } + return result; + } + + @Override + public IntList mutableCopyWithCapacity(int capacity) { + if (capacity < size) { + throw new IllegalArgumentException(); + } + return new IntArrayList(Arrays.copyOf(array, capacity), size); } @Override diff --git a/java/core/src/main/java/com/google/protobuf/Internal.java b/java/core/src/main/java/com/google/protobuf/Internal.java index e19b6dca5..d1de375e1 100644 --- a/java/core/src/main/java/com/google/protobuf/Internal.java +++ b/java/core/src/main/java/com/google/protobuf/Internal.java @@ -41,6 +41,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.RandomAccess; import java.util.Set; /** @@ -51,10 +52,12 @@ import java.util.Set; * * @author kenton@google.com (Kenton Varda) */ -public class Internal { +public final class Internal { - protected static final Charset UTF_8 = Charset.forName("UTF-8"); - protected static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + private Internal() {} + + static final Charset UTF_8 = Charset.forName("UTF-8"); + static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); /** * Helper called by generated code to construct default values for string @@ -406,6 +409,7 @@ public class Internal { public static final CodedInputStream EMPTY_CODED_INPUT_STREAM = CodedInputStream.newInstance(EMPTY_BYTE_ARRAY); + /** * Provides an immutable view of {@code List} around a {@code List}. * @@ -454,10 +458,13 @@ public class Internal { public static Converter newEnumConverter( final EnumLiteMap enumMap, final T unrecognizedValue) { return new Converter() { + @Override public T doForward(Integer value) { T result = enumMap.findValueByNumber(value); return result == null ? unrecognizedValue : result; } + + @Override public Integer doBackward(T value) { return value.getNumber(); } @@ -570,8 +577,10 @@ public class Internal { /** * Extends {@link List} to add the capability to make the list immutable and inspect if it is * modifiable. + *

+ * All implementations must support efficient random access. */ - public static interface ProtobufList extends List { + public static interface ProtobufList extends List, RandomAccess { /** * Makes this list immutable. All subsequent modifications will throw an @@ -583,6 +592,11 @@ public class Internal { * Returns whether this list can be modified via the publicly accessible {@link List} methods. */ boolean isModifiable(); + + /** + * Returns a mutable clone of this list with the specified capacity. + */ + ProtobufList mutableCopyWithCapacity(int capacity); } /** @@ -597,14 +611,20 @@ public class Internal { int getInt(int index); /** - * Like {@link #add(Object)} but more efficient in that it doesn't box the element. + * Like {@link #add(Integer)} but more efficient in that it doesn't box the element. */ void addInt(int element); /** - * Like {@link #set(int, Object)} but more efficient in that it doesn't box the element. + * Like {@link #set(int, Integer)} but more efficient in that it doesn't box the element. */ int setInt(int index, int element); + + /** + * Returns a mutable clone of this list with the specified capacity. + */ + @Override + IntList mutableCopyWithCapacity(int capacity); } /** @@ -619,14 +639,20 @@ public class Internal { boolean getBoolean(int index); /** - * Like {@link #add(Object)} but more efficient in that it doesn't box the element. + * Like {@link #add(Boolean)} but more efficient in that it doesn't box the element. */ void addBoolean(boolean element); /** - * Like {@link #set(int, Object)} but more efficient in that it doesn't box the element. + * Like {@link #set(int, Boolean)} but more efficient in that it doesn't box the element. */ boolean setBoolean(int index, boolean element); + + /** + * Returns a mutable clone of this list with the specified capacity. + */ + @Override + BooleanList mutableCopyWithCapacity(int capacity); } /** @@ -641,14 +667,20 @@ public class Internal { long getLong(int index); /** - * Like {@link #add(Object)} but more efficient in that it doesn't box the element. + * Like {@link #add(Long)} but more efficient in that it doesn't box the element. */ void addLong(long element); /** - * Like {@link #set(int, Object)} but more efficient in that it doesn't box the element. + * Like {@link #set(int, Long)} but more efficient in that it doesn't box the element. */ long setLong(int index, long element); + + /** + * Returns a mutable clone of this list with the specified capacity. + */ + @Override + LongList mutableCopyWithCapacity(int capacity); } /** @@ -663,14 +695,20 @@ public class Internal { double getDouble(int index); /** - * Like {@link #add(Object)} but more efficient in that it doesn't box the element. + * Like {@link #add(Double)} but more efficient in that it doesn't box the element. */ void addDouble(double element); /** - * Like {@link #set(int, Object)} but more efficient in that it doesn't box the element. + * Like {@link #set(int, Double)} but more efficient in that it doesn't box the element. */ double setDouble(int index, double element); + + /** + * Returns a mutable clone of this list with the specified capacity. + */ + @Override + DoubleList mutableCopyWithCapacity(int capacity); } /** @@ -685,13 +723,19 @@ public class Internal { float getFloat(int index); /** - * Like {@link #add(Object)} but more efficient in that it doesn't box the element. + * Like {@link #add(Float)} but more efficient in that it doesn't box the element. */ void addFloat(float element); /** - * Like {@link #set(int, Object)} but more efficient in that it doesn't box the element. + * Like {@link #set(int, Float)} but more efficient in that it doesn't box the element. */ float setFloat(int index, float element); + + /** + * Returns a mutable clone of this list with the specified capacity. + */ + @Override + FloatList mutableCopyWithCapacity(int capacity); } } diff --git a/java/core/src/main/java/com/google/protobuf/LazyField.java b/java/core/src/main/java/com/google/protobuf/LazyField.java index 5e0a485cb..98e13ca19 100644 --- a/java/core/src/main/java/com/google/protobuf/LazyField.java +++ b/java/core/src/main/java/com/google/protobuf/LazyField.java @@ -39,14 +39,14 @@ import java.util.Map.Entry; * * Most of key methods are implemented in {@link LazyFieldLite} but this class * can contain default instance of the message to provide {@code hashCode()}, - * {@code equals()} and {@code toString()}. + * {@code euqals()} and {@code toString()}. * * @author xiangl@google.com (Xiang Li) */ public class LazyField extends LazyFieldLite { /** - * Carry a message's default instance which is used by {@code hashCode()}, {@code equals()} and + * Carry a message's default instance which is used by {@code hashCode()}, {@code euqals()} and * {@code toString()}. */ private final MessageLite defaultInstance; @@ -95,12 +95,12 @@ public class LazyField extends LazyFieldLite { this.entry = entry; } - // @Override + @Override public K getKey() { return entry.getKey(); } - // @Override + @Override public Object getValue() { LazyField field = entry.getValue(); if (field == null) { @@ -113,7 +113,7 @@ public class LazyField extends LazyFieldLite { return entry.getValue(); } - // @Override + @Override public Object setValue(Object value) { if (!(value instanceof MessageLite)) { throw new IllegalArgumentException( @@ -131,13 +131,13 @@ public class LazyField extends LazyFieldLite { this.iterator = iterator; } - // @Override + @Override public boolean hasNext() { return iterator.hasNext(); } + @Override @SuppressWarnings("unchecked") - // @Override public Entry next() { Entry entry = iterator.next(); if (entry.getValue() instanceof LazyField) { @@ -146,7 +146,7 @@ public class LazyField extends LazyFieldLite { return (Entry) entry; } - // @Override + @Override public void remove() { iterator.remove(); } diff --git a/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java b/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java index eea1fe3cd..2febaacec 100644 --- a/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java +++ b/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java @@ -30,14 +30,26 @@ package com.google.protobuf; +import java.io.IOException; + /** * LazyFieldLite encapsulates the logic of lazily parsing message fields. It stores - * the message in a ByteString initially and then parse it on-demand. + * the message in a ByteString initially and then parses it on-demand. * - * LazyField is thread-compatible e.g. concurrent read are safe, however, - * synchronizations are needed under read/write situations. + * LazyFieldLite is thread-compatible: concurrent reads are safe once the proto that this + * LazyFieldLite is a part of is no longer being mutated by its Builder. However, explicit + * synchronization is needed under read/write situations. * - * This class is internal implementation detail, so you don't need to use it directly. + * When a LazyFieldLite is used in the context of a MessageLite object, its behavior is considered + * to be immutable and none of the setter methods in its API are expected to be invoked. All of the + * getters are expected to be thread-safe. When used in the context of a MessageLite.Builder, + * setters can be invoked, but there is no guarantee of thread safety. + * + * TODO(yatin,dweis): Consider splitting this class's functionality and put the mutable methods + * into a separate builder class to allow us to give stronger compile-time guarantees. + * + * This class is internal implementation detail of the protobuf library, so you don't need to use it + * directly. * * @author xiangl@google.com (Xiang Li) */ @@ -46,8 +58,34 @@ public class LazyFieldLite { ExtensionRegistryLite.getEmptyRegistry(); /** - * A delayed-parsed version of the bytes. When this is non-null then {@code extensionRegistry } is - * also non-null and {@code value} and {@code memoizedBytes} are null. + * The value associated with the LazyFieldLite object is stored in one or more of the following + * three fields (delayedBytes, value, memoizedBytes). They should together be interpreted as + * follows. + * 1) delayedBytes can be non-null, while value and memoizedBytes is null. The object will be in + * this state while the value for the object has not yet been parsed. + * 2) Both delayedBytes and value are non-null. The object transitions to this state as soon as + * some caller needs to access the value (by invoking getValue()). + * 3) memoizedBytes is merely an optimization for calls to LazyFieldLite.toByteString() to avoid + * recomputing the ByteString representation on each call. Instead, when the value is parsed + * from delayedBytes, we will also assign the contents of delayedBytes to memoizedBytes (since + * that is the ByteString representation of value). + * 4) Finally, if the LazyFieldLite was created directly with a parsed MessageLite value, then + * delayedBytes will be null, and memoizedBytes will be initialized only upon the first call to + * LazyFieldLite.toByteString(). + * + * Given the above conditions, any caller that needs a serialized representation of this object + * must first check if the memoizedBytes or delayedBytes ByteString is non-null and use it + * directly; if both of those are null, it can look at the parsed value field. Similarly, any + * caller that needs a parsed value must first check if the value field is already non-null, if + * not it must parse the value from delayedBytes. + */ + + /** + * A delayed-parsed version of the contents of this field. When this field is non-null, then the + * "value" field is allowed to be null until the time that the value needs to be read. + * + * When delayedBytes is non-null then {@code extensionRegistry} is required to also be non-null. + * {@code value} and {@code memoizedBytes} will be initialized lazily. */ private ByteString delayedBytes; @@ -60,12 +98,15 @@ public class LazyFieldLite { private ExtensionRegistryLite extensionRegistry; /** - * The parsed value. When this is non-null then {@code delayedBytes} will be null. + * The parsed value. When this is null and a caller needs access to the MessageLite value, then + * {@code delayedBytes} will be parsed lazily at that time. */ protected volatile MessageLite value; /** - * The memoized bytes for {@code value}. Will be null when {@code value} is null. + * The memoized bytes for {@code value}. This is an optimization for the toByteString() method to + * not have to recompute its return-value on each invocation. + * TODO(yatin): Figure out whether this optimization is actually necessary. */ private volatile ByteString memoizedBytes; @@ -94,6 +135,43 @@ public class LazyFieldLite { return lf; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof LazyFieldLite)) { + return false; + } + + LazyFieldLite other = (LazyFieldLite) o; + + // Lazy fields do not work well with equals... If both are delayedBytes, we do not have a + // mechanism to deserialize them so we rely on bytes equality. Otherwise we coerce into an + // actual message (if necessary) and call equals on the message itself. This implies that two + // messages can by unequal but then be turned equal simply be invoking a getter on a lazy field. + MessageLite value1 = value; + MessageLite value2 = other.value; + if (value1 == null && value2 == null) { + return toByteString().equals(other.toByteString()); + } else if (value1 != null && value2 != null) { + return value1.equals(value2); + } else if (value1 != null) { + return value1.equals(other.getValue(value1.getDefaultInstanceForType())); + } else { + return getValue(value2.getDefaultInstanceForType()).equals(value2); + } + } + + @Override + public int hashCode() { + // We can't provide a memoizable hash code for lazy fields. The byte strings may have different + // hash codes but evaluate to equivalent messages. And we have no facility for constructing + // a message here if we were not already holding a value. + return 1; + } + /** * Determines whether this LazyFieldLite instance represents the default instance of this type. */ @@ -230,6 +308,46 @@ public class LazyFieldLite { return; } } + + /** + * Merges another instance's contents from a stream. + * + *

LazyField is not thread-safe for write access. Synchronizations are needed + * under read/write situations. + */ + public void mergeFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws IOException { + if (this.containsDefaultInstance()) { + setByteString(input.readBytes(), extensionRegistry); + return; + } + + // If the other field has an extension registry but this does not, copy over the other extension + // registry. + if (this.extensionRegistry == null) { + this.extensionRegistry = extensionRegistry; + } + + // In the case that both of them are not parsed we simply concatenate the bytes to save time. In + // the (probably rare) case that they have different extension registries there is a chance that + // some of the extensions may be dropped, but the tradeoff of making this operation fast seems + // to outway the benefits of combining the extension registries, which is not normally done for + // lite protos anyways. + if (this.delayedBytes != null) { + setByteString(this.delayedBytes.concat(input.readBytes()), this.extensionRegistry); + return; + } + + // We are parsed and both contain data. We won't drop any extensions here directly, but in the + // case that the extension registries are not the same then we might in the future if we + // need to serialize and parse a message again. + try { + setValue(value.toBuilder().mergeFrom(input, extensionRegistry).build()); + } catch (InvalidProtocolBufferException e) { + // Nothing is logged and no exceptions are thrown. Clients will be unaware that a proto + // was invalid. + } + } private static MessageLite mergeValueAndBytes( MessageLite value, ByteString otherBytes, ExtensionRegistryLite extensionRegistry) { @@ -259,10 +377,12 @@ public class LazyFieldLite { * parsed. Be careful when using this method. */ public int getSerializedSize() { - if (delayedBytes != null) { - return delayedBytes.size(); - } else if (memoizedBytes != null) { + // We *must* return delayed bytes size if it was ever set because the dependent messages may + // have memoized serialized size based off of it. + if (memoizedBytes != null) { return memoizedBytes.size(); + } else if (delayedBytes != null) { + return delayedBytes.size(); } else if (value != null) { return value.getSerializedSize(); } else { @@ -274,12 +394,14 @@ public class LazyFieldLite { * Returns a BytesString for this field in a thread-safe way. */ public ByteString toByteString() { - if (delayedBytes != null) { - return delayedBytes; - } if (memoizedBytes != null) { return memoizedBytes; } + // We *must* return delayed bytes if it was set because the dependent messages may have + // memoized serialized size based off of it. + if (delayedBytes != null) { + return delayedBytes; + } synchronized (this) { if (memoizedBytes != null) { return memoizedBytes; @@ -311,18 +433,15 @@ public class LazyFieldLite { .parseFrom(delayedBytes, extensionRegistry); this.value = parsedValue; this.memoizedBytes = delayedBytes; - this.delayedBytes = null; } else { this.value = defaultInstance; this.memoizedBytes = ByteString.EMPTY; - this.delayedBytes = null; } } catch (InvalidProtocolBufferException e) { // Nothing is logged and no exceptions are thrown. Clients will be unaware that this proto // was invalid. this.value = defaultInstance; this.memoizedBytes = ByteString.EMPTY; - this.delayedBytes = null; } } } diff --git a/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java index c3be3cca8..d474c51ef 100644 --- a/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java +++ b/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java @@ -30,12 +30,12 @@ package com.google.protobuf; -import java.util.Arrays; -import java.util.List; import java.util.AbstractList; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.RandomAccess; /** @@ -64,7 +64,7 @@ import java.util.RandomAccess; */ public class LazyStringArrayList extends AbstractProtobufList implements LazyStringList, RandomAccess { - + private static final LazyStringArrayList EMPTY_LIST = new LazyStringArrayList(); static { EMPTY_LIST.makeImmutable(); @@ -80,11 +80,11 @@ public class LazyStringArrayList extends AbstractProtobufList private final List list; public LazyStringArrayList() { - list = new ArrayList(); + this(DEFAULT_CAPACITY); } public LazyStringArrayList(int intialCapacity) { - list = new ArrayList(intialCapacity); + this(new ArrayList(intialCapacity)); } public LazyStringArrayList(LazyStringList from) { @@ -93,7 +93,21 @@ public class LazyStringArrayList extends AbstractProtobufList } public LazyStringArrayList(List from) { - list = new ArrayList(from); + this(new ArrayList(from)); + } + + private LazyStringArrayList(ArrayList list) { + this.list = list; + } + + @Override + public LazyStringArrayList mutableCopyWithCapacity(int capacity) { + if (capacity < size()) { + throw new IllegalArgumentException(); + } + ArrayList newList = new ArrayList(capacity); + newList.addAll(list); + return new LazyStringArrayList(newList); } @Override @@ -170,7 +184,7 @@ public class LazyStringArrayList extends AbstractProtobufList return ret; } - // @Override + @Override public boolean addAllByteString(Collection values) { ensureIsMutable(); boolean ret = list.addAll(values); @@ -178,7 +192,7 @@ public class LazyStringArrayList extends AbstractProtobufList return ret; } - // @Override + @Override public boolean addAllByteArray(Collection c) { ensureIsMutable(); boolean ret = list.addAll(c); @@ -201,14 +215,14 @@ public class LazyStringArrayList extends AbstractProtobufList modCount++; } - // @Override + @Override public void add(ByteString element) { ensureIsMutable(); list.add(element); modCount++; } - // @Override + @Override public void add(byte[] element) { ensureIsMutable(); list.add(element); @@ -220,7 +234,7 @@ public class LazyStringArrayList extends AbstractProtobufList return list.get(index); } - // @Override + @Override public ByteString getByteString(int index) { Object o = list.get(index); ByteString b = asByteString(o); @@ -230,7 +244,7 @@ public class LazyStringArrayList extends AbstractProtobufList return b; } - // @Override + @Override public byte[] getByteArray(int index) { Object o = list.get(index); byte[] b = asByteArray(o); @@ -240,7 +254,7 @@ public class LazyStringArrayList extends AbstractProtobufList return b; } - // @Override + @Override public void set(int index, ByteString s) { setAndReturn(index, s); } @@ -250,7 +264,7 @@ public class LazyStringArrayList extends AbstractProtobufList return list.set(index, s); } - // @Override + @Override public void set(int index, byte[] s) { setAndReturn(index, s); } @@ -290,12 +304,12 @@ public class LazyStringArrayList extends AbstractProtobufList } } - // @Override + @Override public List getUnderlyingElements() { return Collections.unmodifiableList(list); } - // @Override + @Override public void mergeFrom(LazyStringList other) { ensureIsMutable(); for (Object o : other.getUnderlyingElements()) { @@ -349,7 +363,7 @@ public class LazyStringArrayList extends AbstractProtobufList } } - // @Override + @Override public List asByteArrayList() { return new ByteArrayListView(this); } @@ -393,12 +407,12 @@ public class LazyStringArrayList extends AbstractProtobufList } } - // @Override + @Override public List asByteStringList() { return new ByteStringListView(this); } - // @Override + @Override public LazyStringList getUnmodifiableView() { if (isModifiable()) { return new UnmodifiableLazyStringList(this); diff --git a/java/core/src/main/java/com/google/protobuf/LongArrayList.java b/java/core/src/main/java/com/google/protobuf/LongArrayList.java index ebe620290..bc4475d11 100644 --- a/java/core/src/main/java/com/google/protobuf/LongArrayList.java +++ b/java/core/src/main/java/com/google/protobuf/LongArrayList.java @@ -34,7 +34,6 @@ import com.google.protobuf.Internal.LongList; import java.util.Arrays; import java.util.Collection; -import java.util.List; import java.util.RandomAccess; /** @@ -44,8 +43,6 @@ import java.util.RandomAccess; */ final class LongArrayList extends AbstractProtobufList implements LongList, RandomAccess { - private static final int DEFAULT_CAPACITY = 10; - private static final LongArrayList EMPTY_LIST = new LongArrayList(); static { EMPTY_LIST.makeImmutable(); @@ -70,32 +67,55 @@ final class LongArrayList extends AbstractProtobufList implements LongList * Constructs a new mutable {@code LongArrayList} with default capacity. */ LongArrayList() { - this(DEFAULT_CAPACITY); - } - - /** - * Constructs a new mutable {@code LongArrayList} with the provided capacity. - */ - LongArrayList(int capacity) { - array = new long[capacity]; - size = 0; + this(new long[DEFAULT_CAPACITY], 0); } /** * Constructs a new mutable {@code LongArrayList} containing the same elements as {@code other}. */ - LongArrayList(List other) { - if (other instanceof LongArrayList) { - LongArrayList list = (LongArrayList) other; - array = list.array.clone(); - size = list.size; - } else { - size = other.size(); - array = new long[size]; - for (int i = 0; i < size; i++) { - array[i] = other.get(i); + private LongArrayList(long[] array, int size) { + this.array = array; + this.size = size; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof IntArrayList)) { + return super.equals(o); + } + LongArrayList other = (LongArrayList) o; + if (size != other.size) { + return false; + } + + final long[] arr = other.array; + for (int i = 0; i < size; i++) { + if (array[i] != arr[i]) { + return false; } } + + return true; + } + + @Override + public int hashCode() { + int result = 1; + for (int i = 0; i < size; i++) { + result = (31 * result) + Internal.hashLong(array[i]); + } + return result; + } + + @Override + public LongList mutableCopyWithCapacity(int capacity) { + if (capacity < size) { + throw new IllegalArgumentException(); + } + return new LongArrayList(Arrays.copyOf(array, capacity), size); } @Override diff --git a/java/core/src/main/java/com/google/protobuf/MapEntryLite.java b/java/core/src/main/java/com/google/protobuf/MapEntryLite.java index bcffa9461..12c64abbb 100644 --- a/java/core/src/main/java/com/google/protobuf/MapEntryLite.java +++ b/java/core/src/main/java/com/google/protobuf/MapEntryLite.java @@ -41,7 +41,8 @@ import java.io.IOException; * * Protobuf internal. Users shouldn't use. */ -public class MapEntryLite extends AbstractMessageLite { +public class MapEntryLite + extends AbstractMessageLite, MapEntryLite.Builder> { private static class Metadata { public final MapEntryLite defaultInstance; public final WireFormat.FieldType keyType; @@ -233,7 +234,7 @@ public class MapEntryLite extends AbstractMessageLite { * Builder used to create {@link MapEntryLite} messages. */ public static class Builder - extends AbstractMessageLite.Builder> { + extends AbstractMessageLite.Builder, Builder> { private final Metadata metadata; private K key; private V value; @@ -327,5 +328,10 @@ public class MapEntryLite extends AbstractMessageLite { this.value = entry.value; return this; } + + @Override + protected Builder internalMergeFrom(MapEntryLite message) { + throw new UnsupportedOperationException(); + } } } diff --git a/java/core/src/main/java/com/google/protobuf/MapField.java b/java/core/src/main/java/com/google/protobuf/MapField.java index b290993ca..907f0f711 100644 --- a/java/core/src/main/java/com/google/protobuf/MapField.java +++ b/java/core/src/main/java/com/google/protobuf/MapField.java @@ -93,15 +93,18 @@ public class MapField implements MutabilityOracle { this.defaultEntry = defaultEntry; } + @Override public Message convertKeyAndValueToMessage(K key, V value) { return defaultEntry.newBuilderForType().setKey(key).setValue(value).buildPartial(); } + @Override public void convertMessageToKeyAndValue(Message message, Map map) { MapEntry entry = (MapEntry) message; map.put(entry.getKey(), entry.getValue()); } + @Override public Message getMessageDefaultInstance() { return defaultEntry; } diff --git a/java/core/src/main/java/com/google/protobuf/Message.java b/java/core/src/main/java/com/google/protobuf/Message.java index 9516d71f2..94590fb91 100644 --- a/java/core/src/main/java/com/google/protobuf/Message.java +++ b/java/core/src/main/java/com/google/protobuf/Message.java @@ -51,6 +51,7 @@ import java.util.Map; public interface Message extends MessageLite, MessageOrBuilder { // (From MessageLite, re-declared here only for return type covariance.) + @Override Parser getParserForType(); @@ -97,7 +98,10 @@ public interface Message extends MessageLite, MessageOrBuilder { // Builders // (From MessageLite, re-declared here only for return type covariance.) + @Override Builder newBuilderForType(); + + @Override Builder toBuilder(); /** @@ -106,6 +110,7 @@ public interface Message extends MessageLite, MessageOrBuilder { interface Builder extends MessageLite.Builder, MessageOrBuilder { // (From MessageLite.Builder, re-declared here only for return type // covariance.) + @Override Builder clear(); /** @@ -131,18 +136,27 @@ public interface Message extends MessageLite, MessageOrBuilder { // (From MessageLite.Builder, re-declared here only for return type // covariance.) + @Override Message build(); + + @Override Message buildPartial(); + + @Override Builder clone(); + + @Override Builder mergeFrom(CodedInputStream input) throws IOException; - Builder mergeFrom(CodedInputStream input, - ExtensionRegistryLite extensionRegistry) - throws IOException; + + @Override + Builder mergeFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws IOException; /** * Get the message's type's descriptor. * See {@link Message#getDescriptorForType()}. */ + @Override Descriptors.Descriptor getDescriptorForType(); /** @@ -240,27 +254,39 @@ public interface Message extends MessageLite, MessageOrBuilder { // (From MessageLite.Builder, re-declared here only for return type // covariance.) + @Override Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException; - Builder mergeFrom(ByteString data, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; + + @Override + Builder mergeFrom(ByteString data, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException; + + @Override Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException; - Builder mergeFrom(byte[] data, int off, int len) - throws InvalidProtocolBufferException; - Builder mergeFrom(byte[] data, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; - Builder mergeFrom(byte[] data, int off, int len, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; + + @Override + Builder mergeFrom(byte[] data, int off, int len) throws InvalidProtocolBufferException; + + @Override + Builder mergeFrom(byte[] data, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException; + + @Override + Builder mergeFrom(byte[] data, int off, int len, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException; + + @Override Builder mergeFrom(InputStream input) throws IOException; - Builder mergeFrom(InputStream input, - ExtensionRegistryLite extensionRegistry) - throws IOException; - boolean mergeDelimitedFrom(InputStream input) - throws IOException; - boolean mergeDelimitedFrom(InputStream input, - ExtensionRegistryLite extensionRegistry) - throws IOException; + + @Override + Builder mergeFrom(InputStream input, ExtensionRegistryLite extensionRegistry) + throws IOException; + + @Override + boolean mergeDelimitedFrom(InputStream input) throws IOException; + + @Override + boolean mergeDelimitedFrom(InputStream input, ExtensionRegistryLite extensionRegistry) + throws IOException; } } diff --git a/java/core/src/main/java/com/google/protobuf/MessageLite.java b/java/core/src/main/java/com/google/protobuf/MessageLite.java index 798b79439..88f531df3 100644 --- a/java/core/src/main/java/com/google/protobuf/MessageLite.java +++ b/java/core/src/main/java/com/google/protobuf/MessageLite.java @@ -295,6 +295,27 @@ public interface MessageLite extends MessageLiteOrBuilder { Builder mergeFrom(InputStream input, ExtensionRegistryLite extensionRegistry) throws IOException; + + /** + * Merge {@code other} into the message being built. {@code other} must + * have the exact same type as {@code this} (i.e. + * {@code getClass().equals(getDefaultInstanceForType().getClass())}). + * + * Merging occurs as follows. For each field:
+ * * For singular primitive fields, if the field is set in {@code other}, + * then {@code other}'s value overwrites the value in this message.
+ * * For singular message fields, if the field is set in {@code other}, + * it is merged into the corresponding sub-message of this message + * using the same merging rules.
+ * * For repeated fields, the elements in {@code other} are concatenated + * with the elements in this message. + * * For oneof groups, if the other message has one of the fields set, + * the group of this message is cleared and replaced by the field + * of the other message, so that the oneof constraint is preserved. + * + * This is equivalent to the {@code Message::MergeFrom} method in C++. + */ + Builder mergeFrom(MessageLite other); /** * Like {@link #mergeFrom(InputStream)}, but does not read until EOF. diff --git a/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java b/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java index e69de29bb..43847651f 100644 --- a/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java +++ b/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java @@ -0,0 +1,239 @@ +// 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. + +package com.google.protobuf; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * Helps generate {@link String} representations of {@link MessageLite} protos. + */ +// TODO(dweis): Fix map fields. +final class MessageLiteToString { + + private static final String LIST_SUFFIX = "List"; + private static final String BUILDER_LIST_SUFFIX = "OrBuilderList"; + private static final String BYTES_SUFFIX = "Bytes"; + + /** + * Returns a {@link String} representation of the {@link MessageLite} object. The first line of + * the {@code String} representation representation includes a comment string to uniquely identify + * the objcet instance. This acts as an indicator that this should not be relied on for + * comparisons. + * + *

For use by generated code only. + */ + static String toString(MessageLite messageLite, String commentString) { + StringBuilder buffer = new StringBuilder(); + buffer.append("# ").append(commentString); + reflectivePrintWithIndent(messageLite, buffer, 0); + return buffer.toString(); + } + + /** + * Reflectively prints the {@link MessageLite} to the buffer at given {@code indent} level. + * + * @param buffer the buffer to write to + * @param indent the number of spaces to indent the proto by + */ + private static void reflectivePrintWithIndent( + MessageLite messageLite, StringBuilder buffer, int indent) { + // Build a map of method name to method. We're looking for methods like getFoo(), hasFoo(), and + // getFooList() which might be useful for building an object's string representation. + Map nameToNoArgMethod = new HashMap(); + Map nameToMethod = new HashMap(); + Set getters = new TreeSet(); + for (Method method : messageLite.getClass().getDeclaredMethods()) { + nameToMethod.put(method.getName(), method); + if (method.getParameterTypes().length == 0) { + nameToNoArgMethod.put(method.getName(), method); + + if (method.getName().startsWith("get")) { + getters.add(method.getName()); + } + } + } + + for (String getter : getters) { + String suffix = getter.replaceFirst("get", ""); + if (suffix.endsWith(LIST_SUFFIX) && !suffix.endsWith(BUILDER_LIST_SUFFIX)) { + String camelCase = suffix.substring(0, 1).toLowerCase() + + suffix.substring(1, suffix.length() - LIST_SUFFIX.length()); + // Try to reflectively get the value and toString() the field as if it were repeated. This + // only works if the method names have not be proguarded out or renamed. + Method listMethod = nameToNoArgMethod.get("get" + suffix); + if (listMethod != null) { + printField( + buffer, + indent, + camelCaseToSnakeCase(camelCase), + GeneratedMessageLite.invokeOrDie(listMethod, messageLite)); + continue; + } + } + + Method setter = nameToMethod.get("set" + suffix); + if (setter == null) { + continue; + } + if (suffix.endsWith(BYTES_SUFFIX) + && nameToNoArgMethod.containsKey( + "get" + suffix.substring(0, suffix.length() - "Bytes".length()))) { + // Heuristic to skip bytes based accessors for string fields. + continue; + } + + String camelCase = suffix.substring(0, 1).toLowerCase() + suffix.substring(1); + + // Try to reflectively get the value and toString() the field as if it were optional. This + // only works if the method names have not be proguarded out or renamed. + Method getMethod = nameToNoArgMethod.get("get" + suffix); + Method hasMethod = nameToNoArgMethod.get("has" + suffix); + // TODO(dweis): Fix proto3 semantics. + if (getMethod != null) { + Object value = GeneratedMessageLite.invokeOrDie(getMethod, messageLite); + final boolean hasValue = hasMethod == null + ? !isDefaultValue(value) + : (Boolean) GeneratedMessageLite.invokeOrDie(hasMethod, messageLite); + // TODO(dweis): This doesn't stop printing oneof case twice: value and enum style. + if (hasValue) { + printField( + buffer, + indent, + camelCaseToSnakeCase(camelCase), + value); + } + continue; + } + } + + if (messageLite instanceof GeneratedMessageLite.ExtendableMessage) { + Iterator> iter = + ((GeneratedMessageLite.ExtendableMessage) messageLite).extensions.iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + printField(buffer, indent, "[" + entry.getKey().getNumber() + "]", entry.getValue()); + } + } + + if (((GeneratedMessageLite) messageLite).unknownFields != null) { + ((GeneratedMessageLite) messageLite).unknownFields.printWithIndent(buffer, indent); + } + } + + private static boolean isDefaultValue(Object o) { + if (o instanceof Boolean) { + return !((Boolean) o); + } + if (o instanceof Integer) { + return ((Integer) o) == 0; + } + if (o instanceof Float) { + return ((Float) o) == 0f; + } + if (o instanceof Double) { + return ((Double) o) == 0d; + } + if (o instanceof String) { + return o.equals(""); + } + if (o instanceof ByteString) { + return o.equals(ByteString.EMPTY); + } + if (o instanceof MessageLite) { // Can happen in oneofs. + return o == ((MessageLite) o).getDefaultInstanceForType(); + } + if (o instanceof java.lang.Enum) { // Catches oneof enums. + return ((java.lang.Enum) o).ordinal() == 0; + } + + return false; + } + + /** + * Formats a text proto field. + * + *

For use by generated code only. + * + * @param buffer the buffer to write to + * @param indent the number of spaces the proto should be indented by + * @param name the field name (in lower underscore case) + * @param object the object value of the field + */ + static final void printField(StringBuilder buffer, int indent, String name, Object object) { + if (object instanceof List) { + List list = (List) object; + for (Object entry : list) { + printField(buffer, indent, name, entry); + } + return; + } + + buffer.append('\n'); + for (int i = 0; i < indent; i++) { + buffer.append(' '); + } + buffer.append(name); + + if (object instanceof String) { + buffer.append(": \"").append(TextFormatEscaper.escapeText((String) object)).append('"'); + } else if (object instanceof ByteString) { + buffer.append(": \"").append(TextFormatEscaper.escapeBytes((ByteString) object)).append('"'); + } else if (object instanceof GeneratedMessageLite) { + buffer.append(" {"); + reflectivePrintWithIndent((GeneratedMessageLite) object, buffer, indent + 2); + buffer.append("\n"); + for (int i = 0; i < indent; i++) { + buffer.append(' '); + } + buffer.append("}"); + } else { + buffer.append(": ").append(object.toString()); + } + } + + private static final String camelCaseToSnakeCase(String camelCase) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < camelCase.length(); i++) { + char ch = camelCase.charAt(i); + if (Character.isUpperCase(ch)) { + builder.append("_"); + } + builder.append(Character.toLowerCase(ch)); + } + return builder.toString(); + } +} diff --git a/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java b/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java index f0fc4859e..5e7d78213 100644 --- a/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java +++ b/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java @@ -42,7 +42,7 @@ import java.util.Map; public interface MessageOrBuilder extends MessageLiteOrBuilder { // (From MessageLite, re-declared here only for return type covariance.) - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override Message getDefaultInstanceForType(); /** diff --git a/java/core/src/main/java/com/google/protobuf/MessageReflection.java b/java/core/src/main/java/com/google/protobuf/MessageReflection.java index de4bfd3ef..7b791d9e6 100644 --- a/java/core/src/main/java/com/google/protobuf/MessageReflection.java +++ b/java/core/src/main/java/com/google/protobuf/MessageReflection.java @@ -364,12 +364,14 @@ class MessageReflection { * Finishes the merge and returns the underlying object. */ Object finish(); + } static class BuilderAdapter implements MergeTarget { private final Message.Builder builder; + @Override public Descriptors.Descriptor getDescriptorForType() { return builder.getDescriptorForType(); } @@ -378,6 +380,7 @@ class MessageReflection { this.builder = builder; } + @Override public Object getField(Descriptors.FieldDescriptor field) { return builder.getField(field); } @@ -387,25 +390,27 @@ class MessageReflection { return builder.hasField(field); } - public MergeTarget setField(Descriptors.FieldDescriptor field, - Object value) { + @Override + public MergeTarget setField(Descriptors.FieldDescriptor field, Object value) { builder.setField(field, value); return this; } + @Override public MergeTarget clearField(Descriptors.FieldDescriptor field) { builder.clearField(field); return this; } + @Override public MergeTarget setRepeatedField( Descriptors.FieldDescriptor field, int index, Object value) { builder.setRepeatedField(field, index, value); return this; } - public MergeTarget addRepeatedField( - Descriptors.FieldDescriptor field, Object value) { + @Override + public MergeTarget addRepeatedField(Descriptors.FieldDescriptor field, Object value) { builder.addRepeatedField(field, value); return this; } @@ -426,25 +431,30 @@ class MessageReflection { return builder.getOneofFieldDescriptor(oneof); } + @Override public ContainerType getContainerType() { return ContainerType.MESSAGE; } + @Override public ExtensionRegistry.ExtensionInfo findExtensionByName( ExtensionRegistry registry, String name) { return registry.findImmutableExtensionByName(name); } + @Override public ExtensionRegistry.ExtensionInfo findExtensionByNumber( - ExtensionRegistry registry, Descriptors.Descriptor containingType, - int fieldNumber) { + ExtensionRegistry registry, Descriptors.Descriptor containingType, int fieldNumber) { return registry.findImmutableExtensionByNumber(containingType, fieldNumber); } - public Object parseGroup(CodedInputStream input, + @Override + public Object parseGroup( + CodedInputStream input, ExtensionRegistryLite extensionRegistry, - Descriptors.FieldDescriptor field, Message defaultInstance) + Descriptors.FieldDescriptor field, + Message defaultInstance) throws IOException { Message.Builder subBuilder; // When default instance is not null. The field is an extension field. @@ -463,9 +473,12 @@ class MessageReflection { return subBuilder.buildPartial(); } - public Object parseMessage(CodedInputStream input, + @Override + public Object parseMessage( + CodedInputStream input, ExtensionRegistryLite extensionRegistry, - Descriptors.FieldDescriptor field, Message defaultInstance) + Descriptors.FieldDescriptor field, + Message defaultInstance) throws IOException { Message.Builder subBuilder; // When default instance is not null. The field is an extension field. @@ -484,9 +497,12 @@ class MessageReflection { return subBuilder.buildPartial(); } - public Object parseMessageFromBytes(ByteString bytes, + @Override + public Object parseMessageFromBytes( + ByteString bytes, ExtensionRegistryLite extensionRegistry, - Descriptors.FieldDescriptor field, Message defaultInstance) + Descriptors.FieldDescriptor field, + Message defaultInstance) throws IOException { Message.Builder subBuilder; // When default instance is not null. The field is an extension field. @@ -505,8 +521,9 @@ class MessageReflection { return subBuilder.buildPartial(); } - public MergeTarget newMergeTargetForField(Descriptors.FieldDescriptor field, - Message defaultInstance) { + @Override + public MergeTarget newMergeTargetForField( + Descriptors.FieldDescriptor field, Message defaultInstance) { if (defaultInstance != null) { return new BuilderAdapter( defaultInstance.newBuilderForType()); @@ -515,8 +532,8 @@ class MessageReflection { } } - public WireFormat.Utf8Validation - getUtf8Validation(Descriptors.FieldDescriptor descriptor) { + @Override + public WireFormat.Utf8Validation getUtf8Validation(Descriptors.FieldDescriptor descriptor) { if (descriptor.needsUtf8Check()) { return WireFormat.Utf8Validation.STRICT; } @@ -528,9 +545,11 @@ class MessageReflection { return WireFormat.Utf8Validation.LOOSE; } + @Override public Object finish() { return builder.buildPartial(); } + } @@ -542,38 +561,43 @@ class MessageReflection { this.extensions = extensions; } + @Override public Descriptors.Descriptor getDescriptorForType() { throw new UnsupportedOperationException( "getDescriptorForType() called on FieldSet object"); } + @Override public Object getField(Descriptors.FieldDescriptor field) { return extensions.getField(field); } + @Override public boolean hasField(Descriptors.FieldDescriptor field) { return extensions.hasField(field); } - public MergeTarget setField(Descriptors.FieldDescriptor field, - Object value) { + @Override + public MergeTarget setField(Descriptors.FieldDescriptor field, Object value) { extensions.setField(field, value); return this; } + @Override public MergeTarget clearField(Descriptors.FieldDescriptor field) { extensions.clearField(field); return this; } + @Override public MergeTarget setRepeatedField( Descriptors.FieldDescriptor field, int index, Object value) { extensions.setRepeatedField(field, index, value); return this; } - public MergeTarget addRepeatedField( - Descriptors.FieldDescriptor field, Object value) { + @Override + public MergeTarget addRepeatedField(Descriptors.FieldDescriptor field, Object value) { extensions.addRepeatedField(field, value); return this; } @@ -594,25 +618,31 @@ class MessageReflection { return null; } + @Override public ContainerType getContainerType() { return ContainerType.EXTENSION_SET; } + @Override public ExtensionRegistry.ExtensionInfo findExtensionByName( ExtensionRegistry registry, String name) { return registry.findImmutableExtensionByName(name); } + @Override public ExtensionRegistry.ExtensionInfo findExtensionByNumber( - ExtensionRegistry registry, Descriptors.Descriptor containingType, - int fieldNumber) { + ExtensionRegistry registry, Descriptors.Descriptor containingType, int fieldNumber) { return registry.findImmutableExtensionByNumber(containingType, fieldNumber); } - public Object parseGroup(CodedInputStream input, - ExtensionRegistryLite registry, Descriptors.FieldDescriptor field, - Message defaultInstance) throws IOException { + @Override + public Object parseGroup( + CodedInputStream input, + ExtensionRegistryLite registry, + Descriptors.FieldDescriptor field, + Message defaultInstance) + throws IOException { Message.Builder subBuilder = defaultInstance.newBuilderForType(); if (!field.isRepeated()) { @@ -625,9 +655,13 @@ class MessageReflection { return subBuilder.buildPartial(); } - public Object parseMessage(CodedInputStream input, - ExtensionRegistryLite registry, Descriptors.FieldDescriptor field, - Message defaultInstance) throws IOException { + @Override + public Object parseMessage( + CodedInputStream input, + ExtensionRegistryLite registry, + Descriptors.FieldDescriptor field, + Message defaultInstance) + throws IOException { Message.Builder subBuilder = defaultInstance.newBuilderForType(); if (!field.isRepeated()) { @@ -640,9 +674,13 @@ class MessageReflection { return subBuilder.buildPartial(); } - public Object parseMessageFromBytes(ByteString bytes, - ExtensionRegistryLite registry, Descriptors.FieldDescriptor field, - Message defaultInstance) throws IOException { + @Override + public Object parseMessageFromBytes( + ByteString bytes, + ExtensionRegistryLite registry, + Descriptors.FieldDescriptor field, + Message defaultInstance) + throws IOException { Message.Builder subBuilder = defaultInstance.newBuilderForType(); if (!field.isRepeated()) { Message originalMessage = (Message) getField(field); @@ -654,14 +692,15 @@ class MessageReflection { return subBuilder.buildPartial(); } + @Override public MergeTarget newMergeTargetForField( Descriptors.FieldDescriptor descriptor, Message defaultInstance) { throw new UnsupportedOperationException( "newMergeTargetForField() called on FieldSet object"); } - public WireFormat.Utf8Validation - getUtf8Validation(Descriptors.FieldDescriptor descriptor) { + @Override + public WireFormat.Utf8Validation getUtf8Validation(Descriptors.FieldDescriptor descriptor) { if (descriptor.needsUtf8Check()) { return WireFormat.Utf8Validation.STRICT; } @@ -669,10 +708,12 @@ class MessageReflection { return WireFormat.Utf8Validation.LOOSE; } + @Override public Object finish() { throw new UnsupportedOperationException( "finish() called on FieldSet object"); } + } /** diff --git a/java/core/src/main/java/com/google/protobuf/NioByteString.java b/java/core/src/main/java/com/google/protobuf/NioByteString.java index f71e41b26..6163c7b1a 100644 --- a/java/core/src/main/java/com/google/protobuf/NioByteString.java +++ b/java/core/src/main/java/com/google/protobuf/NioByteString.java @@ -30,15 +30,14 @@ package com.google.protobuf; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.OutputStream; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.InvalidMarkException; -import java.nio.channels.Channels; import java.nio.charset.Charset; import java.util.Collections; import java.util.List; @@ -54,7 +53,7 @@ final class NioByteString extends ByteString.LeafByteString { throw new NullPointerException("buffer"); } - this.buffer = buffer.slice(); + this.buffer = buffer.slice().order(ByteOrder.nativeOrder()); } // ================================================================= @@ -119,7 +118,7 @@ final class NioByteString extends ByteString.LeafByteString { @Override public void writeTo(OutputStream out) throws IOException { - writeToInternal(out, buffer.position(), buffer.remaining()); + out.write(toByteArray()); } @Override @@ -137,14 +136,12 @@ final class NioByteString extends ByteString.LeafByteString { return; } - // Slow path - if (out instanceof FileOutputStream || numberToWrite >= 8192) { - // Use a channel to write out the ByteBuffer. - Channels.newChannel(out).write(slice(sourceOffset, sourceOffset + numberToWrite)); - } else { - // Just copy the data to an array and write it. - out.write(toByteArray()); - } + ByteBufferWriter.write(slice(sourceOffset, sourceOffset + numberToWrite), out); + } + + @Override + void writeTo(ByteOutput output) throws IOException { + output.writeLazy(buffer.slice()); } @Override @@ -159,46 +156,30 @@ final class NioByteString extends ByteString.LeafByteString { @Override protected String toStringInternal(Charset charset) { - byte[] bytes; - int offset; + final byte[] bytes; + final int offset; + final int length; if (buffer.hasArray()) { bytes = buffer.array(); offset = buffer.arrayOffset() + buffer.position(); + length = buffer.remaining(); } else { + // TODO(nathanmittler): Can we optimize this? bytes = toByteArray(); offset = 0; + length = bytes.length; } - return new String(bytes, offset, size(), charset); + return new String(bytes, offset, length, charset); } @Override public boolean isValidUtf8() { - // TODO(nathanmittler): add a ByteBuffer fork for Utf8.isValidUtf8 to avoid the copy - byte[] bytes; - int startIndex; - if (buffer.hasArray()) { - bytes = buffer.array(); - startIndex = buffer.arrayOffset() + buffer.position(); - } else { - bytes = toByteArray(); - startIndex = 0; - } - return Utf8.isValidUtf8(bytes, startIndex, startIndex + size()); + return Utf8.isValidUtf8(buffer); } @Override protected int partialIsValidUtf8(int state, int offset, int length) { - // TODO(nathanmittler): TODO add a ByteBuffer fork for Utf8.partialIsValidUtf8 to avoid the copy - byte[] bytes; - int startIndex; - if (buffer.hasArray()) { - bytes = buffer.array(); - startIndex = buffer.arrayOffset() + buffer.position(); - } else { - bytes = toByteArray(); - startIndex = 0; - } - return Utf8.partialIsValidUtf8(state, bytes, startIndex, startIndex + size()); + return Utf8.partialIsValidUtf8(state, buffer, offset, offset + length); } @Override diff --git a/java/core/src/main/java/com/google/protobuf/Parser.java b/java/core/src/main/java/com/google/protobuf/Parser.java index 3fa11c3bc..6db692472 100644 --- a/java/core/src/main/java/com/google/protobuf/Parser.java +++ b/java/core/src/main/java/com/google/protobuf/Parser.java @@ -30,7 +30,6 @@ package com.google.protobuf; -import java.io.IOException; import java.io.InputStream; /** diff --git a/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java b/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java index d2f82ac51..81255ec29 100644 --- a/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java +++ b/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java @@ -38,7 +38,7 @@ import java.util.List; /** * Implements {@link ProtobufList} for non-primitive and {@link String} types. */ -class ProtobufArrayList extends AbstractProtobufList { +final class ProtobufArrayList extends AbstractProtobufList { private static final ProtobufArrayList EMPTY_LIST = new ProtobufArrayList(); static { @@ -51,17 +51,23 @@ class ProtobufArrayList extends AbstractProtobufList { } private final List list; - + ProtobufArrayList() { - list = new ArrayList(); + this(new ArrayList(DEFAULT_CAPACITY)); } - ProtobufArrayList(List toCopy) { - list = new ArrayList(toCopy); + private ProtobufArrayList(List list) { + this.list = list; } - - ProtobufArrayList(int capacity) { - list = new ArrayList(capacity); + + @Override + public ProtobufArrayList mutableCopyWithCapacity(int capacity) { + if (capacity < size()) { + throw new IllegalArgumentException(); + } + List newList = new ArrayList(capacity); + newList.addAll(list); + return new ProtobufArrayList(newList); } @Override diff --git a/java/core/src/main/java/com/google/protobuf/ProtocolMessageEnum.java b/java/core/src/main/java/com/google/protobuf/ProtocolMessageEnum.java index 0c8df9899..a596d3019 100644 --- a/java/core/src/main/java/com/google/protobuf/ProtocolMessageEnum.java +++ b/java/core/src/main/java/com/google/protobuf/ProtocolMessageEnum.java @@ -42,6 +42,7 @@ public interface ProtocolMessageEnum extends Internal.EnumLite { /** * Return the value's numeric value as defined in the .proto file. */ + @Override int getNumber(); /** diff --git a/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java b/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java index f91cdbced..29f567dcd 100644 --- a/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java +++ b/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java @@ -579,7 +579,7 @@ public class RepeatedFieldBuilder } } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void markDirty() { onChanged(); } @@ -621,10 +621,12 @@ public class RepeatedFieldBuilder this.builder = builder; } + @Override public int size() { return this.builder.getCount(); } + @Override public MType get(int index) { return builder.getMessage(index); } @@ -654,10 +656,12 @@ public class RepeatedFieldBuilder this.builder = builder; } + @Override public int size() { return this.builder.getCount(); } + @Override public BType get(int index) { return builder.getBuilder(index); } @@ -687,10 +691,12 @@ public class RepeatedFieldBuilder this.builder = builder; } + @Override public int size() { return this.builder.getCount(); } + @Override public IType get(int index) { return builder.getMessageOrBuilder(index); } diff --git a/java/core/src/main/java/com/google/protobuf/RopeByteString.java b/java/core/src/main/java/com/google/protobuf/RopeByteString.java index 8badfabdc..3f3e9bd15 100644 --- a/java/core/src/main/java/com/google/protobuf/RopeByteString.java +++ b/java/core/src/main/java/com/google/protobuf/RopeByteString.java @@ -48,10 +48,11 @@ import java.util.Stack; /** * Class to represent {@code ByteStrings} formed by concatenation of other * ByteStrings, without copying the data in the pieces. The concatenation is - * represented as a tree whose leaf nodes are each a {@link LiteralByteString}. + * represented as a tree whose leaf nodes are each a + * {@link com.google.protobuf.ByteString.LeafByteString}. * *

Most of the operation here is inspired by the now-famous paper + * href="https://web.archive.org/web/20060202015456/http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf"> * BAP95 Ropes: an Alternative to Strings hans-j. boehm, russ atkinson and * michael plass * @@ -139,8 +140,9 @@ final class RopeByteString extends ByteString { /** * Concatenate the given strings while performing various optimizations to * slow the growth rate of tree depth and tree node count. The result is - * either a {@link LiteralByteString} or a {@link RopeByteString} - * depending on which optimizations, if any, were applied. + * either a {@link com.google.protobuf.ByteString.LeafByteString} or a + * {@link RopeByteString} depending on which optimizations, if any, were + * applied. * *

Small pieces of length less than {@link * ByteString#CONCATENATE_BY_COPY_SIZE} may be copied by value here, as in @@ -294,8 +296,7 @@ final class RopeByteString extends ByteString { * *

Substrings of {@code length < 2} should result in at most a single * recursive call chain, terminating at a leaf node. Thus the result will be a - * {@link LiteralByteString}. {@link #RopeByteString(ByteString, - * ByteString)}. + * {@link com.google.protobuf.ByteString.LeafByteString}. * * @param beginIndex start at this index * @param endIndex the last character is the one before this index @@ -368,7 +369,7 @@ final class RopeByteString extends ByteString { @Override public List asReadOnlyByteBufferList() { - // Walk through the list of LiteralByteString's that make up this + // Walk through the list of LeafByteString's that make up this // rope, and add each one as a read-only ByteBuffer. List result = new ArrayList(); PieceIterator pieces = new PieceIterator(this); @@ -399,6 +400,12 @@ final class RopeByteString extends ByteString { } } + @Override + void writeTo(ByteOutput output) throws IOException { + left.writeTo(output); + right.writeTo(output); + } + @Override protected String toStringInternal(Charset charset) { return new String(toByteArray(), charset); @@ -709,9 +716,10 @@ final class RopeByteString extends ByteString { } /** - * Returns the next item and advances one {@code LiteralByteString}. + * Returns the next item and advances one + * {@link com.google.protobuf.ByteString.LeafByteString}. * - * @return next non-empty LiteralByteString or {@code null} + * @return next non-empty LeafByteString or {@code null} */ @Override public LeafByteString next() { diff --git a/java/core/src/main/java/com/google/protobuf/RpcUtil.java b/java/core/src/main/java/com/google/protobuf/RpcUtil.java index 694b8d132..f7d555ae1 100644 --- a/java/core/src/main/java/com/google/protobuf/RpcUtil.java +++ b/java/core/src/main/java/com/google/protobuf/RpcUtil.java @@ -71,6 +71,7 @@ public final class RpcUtil { final Class originalClass, final Type defaultInstance) { return new RpcCallback() { + @Override public void run(final Message parameter) { Type typedParameter; try { @@ -107,8 +108,9 @@ public final class RpcUtil { return new RpcCallback() { private boolean alreadyCalled = false; + @Override public void run(final ParameterType parameter) { - synchronized(this) { + synchronized (this) { if (alreadyCalled) { throw new AlreadyCalledException(); } diff --git a/java/core/src/main/java/com/google/protobuf/SingleFieldBuilder.java b/java/core/src/main/java/com/google/protobuf/SingleFieldBuilder.java index aba65e322..941b5defc 100644 --- a/java/core/src/main/java/com/google/protobuf/SingleFieldBuilder.java +++ b/java/core/src/main/java/com/google/protobuf/SingleFieldBuilder.java @@ -234,7 +234,7 @@ public class SingleFieldBuilder } } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void markDirty() { onChanged(); } diff --git a/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java b/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java index 0674d2e28..409fec10e 100644 --- a/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java +++ b/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java @@ -35,12 +35,12 @@ import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; -import java.util.TreeMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.SortedMap; +import java.util.TreeMap; /** * A custom map implementation from FieldDescriptor to Object optimized to @@ -411,22 +411,22 @@ class SmallSortedMap, V> extends AbstractMap { this.value = value; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public K getKey() { return key; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public V getValue() { return value; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public int compareTo(Entry other) { return getKey().compareTo(other.getKey()); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public V setValue(V newValue) { checkMutable(); final V oldValue = this.value; @@ -535,13 +535,13 @@ class SmallSortedMap, V> extends AbstractMap { private boolean nextCalledBeforeRemove; private Iterator> lazyOverflowIterator; - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public boolean hasNext() { return (pos + 1) < entryList.size() || getOverflowIterator().hasNext(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public Map.Entry next() { nextCalledBeforeRemove = true; // Always increment pos so that we know whether the last returned value @@ -552,7 +552,7 @@ class SmallSortedMap, V> extends AbstractMap { return getOverflowIterator().next(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void remove() { if (!nextCalledBeforeRemove) { throw new IllegalStateException("remove() was called before next()"); @@ -588,31 +588,83 @@ class SmallSortedMap, V> extends AbstractMap { */ private static class EmptySet { - private static final Iterator ITERATOR = new Iterator() { - //@Override (Java 1.6 override semantics, but we must support 1.5) - public boolean hasNext() { - return false; - } - //@Override (Java 1.6 override semantics, but we must support 1.5) - public Object next() { - throw new NoSuchElementException(); - } - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void remove() { - throw new UnsupportedOperationException(); - } - }; + private static final Iterator ITERATOR = + new Iterator() { + @Override + public boolean hasNext() { + return false; + } + @Override + public Object next() { + throw new NoSuchElementException(); + } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; - private static final Iterable ITERABLE = new Iterable() { - //@Override (Java 1.6 override semantics, but we must support 1.5) - public Iterator iterator() { - return ITERATOR; - } - }; + private static final Iterable ITERABLE = + new Iterable() { + @Override + public Iterator iterator() { + return ITERATOR; + } + }; @SuppressWarnings("unchecked") static Iterable iterable() { return (Iterable) ITERABLE; } } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof SmallSortedMap)) { + return super.equals(o); + } + + SmallSortedMap other = (SmallSortedMap) o; + final int size = size(); + if (size != other.size()) { + return false; + } + + // Best effort try to avoid allocating an entry set. + final int numArrayEntries = getNumArrayEntries(); + if (numArrayEntries != other.getNumArrayEntries()) { + return entrySet().equals(other.entrySet()); + } + + for (int i = 0; i < numArrayEntries; i++) { + if (!getArrayEntryAt(i).equals(other.getArrayEntryAt(i))) { + return false; + } + } + + if (numArrayEntries != size) { + return overflowEntries.equals(other.overflowEntries); + } + + + return true; + } + + @Override + public int hashCode() { + int h = 0; + final int listSize = getNumArrayEntries(); + for (int i = 0; i < listSize; i++) { + h += entryList.get(i).hashCode(); + } + // Avoid the iterator allocation if possible. + if (getNumOverflowEntries() > 0) { + h += overflowEntries.hashCode(); + } + return h; + } } 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 c99b52851..c1c328fc7 100644 --- a/java/core/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/core/src/main/java/com/google/protobuf/TextFormat.java @@ -425,7 +425,7 @@ public final class TextFormat { case STRING: generator.print("\""); generator.print(escapeNonAscii - ? escapeText((String) value) + ? TextFormatEscaper.escapeText((String) value) : escapeDoubleQuotesAndBackslashes((String) value) .replace("\n", "\\n")); generator.print("\""); @@ -661,6 +661,14 @@ public final class TextFormat { nextToken(); } + int getLine() { + return line; + } + + int getColumn() { + return column; + } + /** Are we at the end of the input? */ public boolean atEnd() { return currentToken.length() == 0; @@ -957,11 +965,13 @@ public final class TextFormat { */ public boolean consumeBoolean() throws ParseException { if (currentToken.equals("true") + || currentToken.equals("True") || currentToken.equals("t") || currentToken.equals("1")) { nextToken(); return true; } else if (currentToken.equals("false") + || currentToken.equals("False") || currentToken.equals("f") || currentToken.equals("0")) { nextToken(); @@ -1074,7 +1084,7 @@ public final class TextFormat { private ParseException floatParseException(final NumberFormatException e) { return parseException("Couldn't parse number: " + e.getMessage()); } - + /** * Returns a {@link UnknownFieldParseException} with the line and column * numbers of the previous token in the description, and the unknown field @@ -1133,7 +1143,7 @@ public final class TextFormat { return column; } } - + /** * Thrown when encountering an unknown field while parsing * a text format message. @@ -1257,11 +1267,14 @@ public final class TextFormat { private final boolean allowUnknownFields; private final SingularOverwritePolicy singularOverwritePolicy; + private TextFormatParseInfoTree.Builder parseInfoTreeBuilder; - private Parser(boolean allowUnknownFields, - SingularOverwritePolicy singularOverwritePolicy) { + private Parser( + boolean allowUnknownFields, SingularOverwritePolicy singularOverwritePolicy, + TextFormatParseInfoTree.Builder parseInfoTreeBuilder) { this.allowUnknownFields = allowUnknownFields; this.singularOverwritePolicy = singularOverwritePolicy; + this.parseInfoTreeBuilder = parseInfoTreeBuilder; } /** @@ -1278,6 +1291,7 @@ public final class TextFormat { private boolean allowUnknownFields = false; private SingularOverwritePolicy singularOverwritePolicy = SingularOverwritePolicy.ALLOW_SINGULAR_OVERWRITES; + private TextFormatParseInfoTree.Builder parseInfoTreeBuilder = null; /** @@ -1288,8 +1302,15 @@ public final class TextFormat { return this; } + public Builder setParseInfoTreeBuilder( + TextFormatParseInfoTree.Builder parseInfoTreeBuilder) { + this.parseInfoTreeBuilder = parseInfoTreeBuilder; + return this; + } + public Parser build() { - return new Parser(allowUnknownFields, singularOverwritePolicy); + return new Parser( + allowUnknownFields, singularOverwritePolicy, parseInfoTreeBuilder); } } @@ -1380,7 +1401,21 @@ public final class TextFormat { final ExtensionRegistry extensionRegistry, final MessageReflection.MergeTarget target) throws ParseException { + mergeField(tokenizer, extensionRegistry, target, parseInfoTreeBuilder); + } + + /** + * Parse a single field from {@code tokenizer} and merge it into + * {@code builder}. + */ + private void mergeField(final Tokenizer tokenizer, + final ExtensionRegistry extensionRegistry, + final MessageReflection.MergeTarget target, + TextFormatParseInfoTree.Builder parseTreeBuilder) + throws ParseException { FieldDescriptor field = null; + int startLine = tokenizer.getLine(); + int startColumn = tokenizer.getColumn(); final Descriptor type = target.getDescriptorForType(); ExtensionRegistry.ExtensionInfo extension = null; @@ -1472,14 +1507,51 @@ public final class TextFormat { // Handle potential ':'. if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { tokenizer.tryConsume(":"); // optional + if (parseTreeBuilder != null) { + TextFormatParseInfoTree.Builder childParseTreeBuilder = + parseTreeBuilder.getBuilderForSubMessageField(field); + consumeFieldValues(tokenizer, extensionRegistry, target, field, extension, + childParseTreeBuilder); + } else { + consumeFieldValues(tokenizer, extensionRegistry, target, field, extension, + parseTreeBuilder); + } } else { tokenizer.consume(":"); // required + consumeFieldValues( + tokenizer, extensionRegistry, target, field, extension, parseTreeBuilder); } + + if (parseTreeBuilder != null) { + parseTreeBuilder.setLocation( + field, TextFormatParseLocation.create(startLine, startColumn)); + } + + // For historical reasons, fields may optionally be separated by commas or + // semicolons. + if (!tokenizer.tryConsume(";")) { + tokenizer.tryConsume(","); + } + } + + /** + * Parse a one or more field values from {@code tokenizer} and merge it into + * {@code builder}. + */ + private void consumeFieldValues( + final Tokenizer tokenizer, + final ExtensionRegistry extensionRegistry, + final MessageReflection.MergeTarget target, + final FieldDescriptor field, + final ExtensionRegistry.ExtensionInfo extension, + final TextFormatParseInfoTree.Builder parseTreeBuilder) + throws ParseException { // Support specifying repeated field values as a comma-separated list. // Ex."foo: [1, 2, 3]" if (field.isRepeated() && tokenizer.tryConsume("[")) { while (true) { - consumeFieldValue(tokenizer, extensionRegistry, target, field, extension); + consumeFieldValue(tokenizer, extensionRegistry, target, field, extension, + parseTreeBuilder); if (tokenizer.tryConsume("]")) { // End of list. break; @@ -1487,13 +1559,8 @@ public final class TextFormat { tokenizer.consume(","); } } else { - consumeFieldValue(tokenizer, extensionRegistry, target, field, extension); - } - - // For historical reasons, fields may optionally be separated by commas or - // semicolons. - if (!tokenizer.tryConsume(";")) { - tokenizer.tryConsume(","); + consumeFieldValue( + tokenizer, extensionRegistry, target, field, extension, parseTreeBuilder); } } @@ -1506,7 +1573,8 @@ public final class TextFormat { final ExtensionRegistry extensionRegistry, final MessageReflection.MergeTarget target, final FieldDescriptor field, - final ExtensionRegistry.ExtensionInfo extension) + final ExtensionRegistry.ExtensionInfo extension, + final TextFormatParseInfoTree.Builder parseTreeBuilder) throws ParseException { Object value = null; @@ -1528,7 +1596,7 @@ public final class TextFormat { throw tokenizer.parseException( "Expected \"" + endToken + "\"."); } - mergeField(tokenizer, extensionRegistry, subField); + mergeField(tokenizer, extensionRegistry, subField, parseTreeBuilder); } value = subField.finish(); @@ -1704,11 +1772,6 @@ public final class TextFormat { // Some of these methods are package-private because Descriptors.java uses // them. - private interface ByteSequence { - int size(); - byte byteAt(int offset); - } - /** * Escapes bytes in the format used in protocol buffer text format, which * is the same as the format used for C string literals. All bytes @@ -1717,74 +1780,15 @@ public final class TextFormat { * which no defined short-hand escape sequence is defined will be escaped * using 3-digit octal sequences. */ - public static String escapeBytes(final ByteSequence input) { - final StringBuilder builder = new StringBuilder(input.size()); - for (int i = 0; i < input.size(); i++) { - final byte b = input.byteAt(i); - switch (b) { - // Java does not recognize \a or \v, apparently. - case 0x07: builder.append("\\a"); break; - case '\b': builder.append("\\b"); break; - case '\f': builder.append("\\f"); break; - case '\n': builder.append("\\n"); break; - case '\r': builder.append("\\r"); break; - case '\t': builder.append("\\t"); break; - case 0x0b: builder.append("\\v"); break; - case '\\': builder.append("\\\\"); break; - case '\'': builder.append("\\\'"); break; - case '"' : builder.append("\\\""); break; - default: - // Only ASCII characters between 0x20 (space) and 0x7e (tilde) are - // printable. Other byte values must be escaped. - if (b >= 0x20 && b <= 0x7e) { - builder.append((char) b); - } else { - builder.append('\\'); - builder.append((char) ('0' + ((b >>> 6) & 3))); - builder.append((char) ('0' + ((b >>> 3) & 7))); - builder.append((char) ('0' + (b & 7))); - } - break; - } - } - return builder.toString(); - } - - /** - * Escapes bytes in the format used in protocol buffer text format, which - * is the same as the format used for C string literals. All bytes - * that are not printable 7-bit ASCII characters are escaped, as well as - * backslash, single-quote, and double-quote characters. Characters for - * which no defined short-hand escape sequence is defined will be escaped - * using 3-digit octal sequences. - */ - public static String escapeBytes(final ByteString input) { - return escapeBytes(new ByteSequence() { - @Override - public int size() { - return input.size(); - } - @Override - public byte byteAt(int offset) { - return input.byteAt(offset); - } - }); + public static String escapeBytes(ByteString input) { + return TextFormatEscaper.escapeBytes(input); } /** * Like {@link #escapeBytes(ByteString)}, but used for byte array. */ - public static String escapeBytes(final byte[] input) { - return escapeBytes(new ByteSequence() { - @Override - public int size() { - return input.length; - } - @Override - public byte byteAt(int offset) { - return input[offset]; - } - }); + public static String escapeBytes(byte[] input) { + return TextFormatEscaper.escapeBytes(input); } /** @@ -1868,7 +1872,9 @@ public final class TextFormat { } } - return ByteString.copyFrom(result, 0, pos); + return result.length == pos + ? ByteString.wrap(result) // This reference has not been out of our control. + : ByteString.copyFrom(result, 0, pos); } /** @@ -1896,7 +1902,7 @@ public final class TextFormat { * Escape double quotes and backslashes in a String for unicode output of a message. */ public static String escapeDoubleQuotesAndBackslashes(final String input) { - return input.replace("\\", "\\\\").replace("\"", "\\\""); + return TextFormatEscaper.escapeDoubleQuotesAndBackslashes(input); } /** diff --git a/java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java b/java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java index e69de29bb..da9ceadd1 100644 --- a/java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java +++ b/java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java @@ -0,0 +1,137 @@ +// 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. + +package com.google.protobuf; + +/** + * Provide text format escaping support for proto2 instances. + */ +final class TextFormatEscaper { + private TextFormatEscaper() {} + + private interface ByteSequence { + int size(); + byte byteAt(int offset); + } + + /** + * Escapes bytes in the format used in protocol buffer text format, which + * is the same as the format used for C string literals. All bytes + * that are not printable 7-bit ASCII characters are escaped, as well as + * backslash, single-quote, and double-quote characters. Characters for + * which no defined short-hand escape sequence is defined will be escaped + * using 3-digit octal sequences. + */ + static String escapeBytes(final ByteSequence input) { + final StringBuilder builder = new StringBuilder(input.size()); + for (int i = 0; i < input.size(); i++) { + final byte b = input.byteAt(i); + switch (b) { + // Java does not recognize \a or \v, apparently. + case 0x07: builder.append("\\a"); break; + case '\b': builder.append("\\b"); break; + case '\f': builder.append("\\f"); break; + case '\n': builder.append("\\n"); break; + case '\r': builder.append("\\r"); break; + case '\t': builder.append("\\t"); break; + case 0x0b: builder.append("\\v"); break; + case '\\': builder.append("\\\\"); break; + case '\'': builder.append("\\\'"); break; + case '"' : builder.append("\\\""); break; + default: + // Only ASCII characters between 0x20 (space) and 0x7e (tilde) are + // printable. Other byte values must be escaped. + if (b >= 0x20 && b <= 0x7e) { + builder.append((char) b); + } else { + builder.append('\\'); + builder.append((char) ('0' + ((b >>> 6) & 3))); + builder.append((char) ('0' + ((b >>> 3) & 7))); + builder.append((char) ('0' + (b & 7))); + } + break; + } + } + return builder.toString(); + } + + /** + * Escapes bytes in the format used in protocol buffer text format, which + * is the same as the format used for C string literals. All bytes + * that are not printable 7-bit ASCII characters are escaped, as well as + * backslash, single-quote, and double-quote characters. Characters for + * which no defined short-hand escape sequence is defined will be escaped + * using 3-digit octal sequences. + */ + static String escapeBytes(final ByteString input) { + return escapeBytes(new ByteSequence() { + @Override + public int size() { + return input.size(); + } + @Override + public byte byteAt(int offset) { + return input.byteAt(offset); + } + }); + } + + /** + * Like {@link #escapeBytes(ByteString)}, but used for byte array. + */ + static String escapeBytes(final byte[] input) { + return escapeBytes(new ByteSequence() { + @Override + public int size() { + return input.length; + } + @Override + public byte byteAt(int offset) { + return input[offset]; + } + }); + } + + /** + * Like {@link #escapeBytes(ByteString)}, but escapes a text string. + * Non-ASCII characters are first encoded as UTF-8, then each byte is escaped + * individually as a 3-digit octal escape. Yes, it's weird. + */ + static String escapeText(final String input) { + return escapeBytes(ByteString.copyFromUtf8(input)); + } + + /** + * Escape double quotes and backslashes in a String for unicode output of a message. + */ + static String escapeDoubleQuotesAndBackslashes(final String input) { + return input.replace("\\", "\\\\").replace("\"", "\\\""); + } +} diff --git a/java/core/src/main/java/com/google/protobuf/TextFormatParseInfoTree.java b/java/core/src/main/java/com/google/protobuf/TextFormatParseInfoTree.java new file mode 100644 index 000000000..2ecf912e6 --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/TextFormatParseInfoTree.java @@ -0,0 +1,225 @@ +// 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. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.FieldDescriptor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + + +/** + * Data structure which is populated with the locations of each field value parsed from the text. + * + *

The locations of primary fields values are retrieved by {@code getLocation} or + * {@code getLocations}. The locations of sub message values are within nested + * {@code TextFormatParseInfoTree}s and are retrieve by {@getNestedTree} or {code @getNestedTrees}. + * + *

The {@code TextFormatParseInfoTree} is created by a Builder. + */ +public class TextFormatParseInfoTree { + + // Defines a mapping between each field's descriptor to the list of locations where + // its value(s) were was encountered. + private Map> locationsFromField; + + // Defines a mapping between a field's descriptor to a list of TextFormatParseInfoTrees for + // sub message location information. + Map> subtreesFromField; + + /** + * Construct a {@code TextFormatParseInfoTree}. + * + * @param locationsFromField a map of fields to location in the source code + * @param subtreeBuildersFromField a map of fields to parse tree location information builders + */ + private TextFormatParseInfoTree( + Map> locationsFromField, + Map> subtreeBuildersFromField) { + + // The maps are unmodifiable. The values in the maps are unmodifiable. + Map> locs = + new HashMap>(); + for (Entry> kv : locationsFromField.entrySet()) { + locs.put(kv.getKey(), Collections.unmodifiableList(kv.getValue())); + } + this.locationsFromField = Collections.unmodifiableMap(locs); + + Map> subs = + new HashMap>(); + for (Entry> kv : subtreeBuildersFromField.entrySet()) { + List submessagesOfField = new ArrayList(); + for (Builder subBuilder : kv.getValue()) { + submessagesOfField.add(subBuilder.build()); + } + subs.put(kv.getKey(), Collections.unmodifiableList(submessagesOfField)); + } + this.subtreesFromField = Collections.unmodifiableMap(subs); + } + + /** + * Retrieve all the locations of a field. + * + * @param fieldDescriptor the the @{link FieldDescriptor} of the desired field + * @return a list of the locations of values of the field. If there are not values + * or the field doesn't exist, an empty list is returned. + */ + public List getLocations(final FieldDescriptor fieldDescriptor) { + List result = locationsFromField.get(fieldDescriptor); + return (result == null) ? Collections.emptyList() : result; + } + + /** + * Get the location in the source of a field's value. + * + *

Returns the {@link TextFormatParseLocation} for index-th value of the field in the parsed + * text. + * + * @param fieldDescriptor the @{link FieldDescriptor} of the desired field + * @param index the index of the value. + * @return the {@link TextFormatParseLocation} of the value + * @throws IllegalArgumentException index is out of range + */ + public TextFormatParseLocation getLocation(final FieldDescriptor fieldDescriptor, int index) { + return getFromList(getLocations(fieldDescriptor), index, fieldDescriptor); + } + + /** + * Retrieve a list of all the location information trees for a sub message field. + * + * @param fieldDescriptor the @{link FieldDescriptor} of the desired field + * @return A list of {@link TextFormatParseInfoTree} + */ + public List getNestedTrees(final FieldDescriptor fieldDescriptor) { + List result = subtreesFromField.get(fieldDescriptor); + return result == null ? Collections.emptyList() : result; + } + + /** + * Returns the parse info tree for the given field, which must be a message type. + * + * @param fieldDescriptor the @{link FieldDescriptor} of the desired sub message + * @param index the index of message value. + * @return the {@code ParseInfoTree} of the message value. {@code null} is returned if the field + * doesn't exist or the index is out of range. + * @throws IllegalArgumentException if index is out of range + */ + public TextFormatParseInfoTree getNestedTree(final FieldDescriptor fieldDescriptor, int index) { + return getFromList(getNestedTrees(fieldDescriptor), index, fieldDescriptor); + } + + /** + * Create a builder for a {@code ParseInfoTree}. + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + + private static T getFromList(List list, int index, FieldDescriptor fieldDescriptor) { + if (index >= list.size() || index < 0) { + throw new IllegalArgumentException(String.format("Illegal index field: %s, index %d", + fieldDescriptor == null ? "" : fieldDescriptor.getName(), index)); + } + return list.get(index); + } + + /** + * Builder for a {@link TextFormatParseInfoTree}. + */ + public static class Builder { + + private Map> locationsFromField; + + // Defines a mapping between a field's descriptor to a list of ParseInfoTrees builders for + // sub message location information. + private Map> subtreeBuildersFromField; + + /** + * Create a root level {@ParseInfoTree} builder. + */ + private Builder() { + locationsFromField = new HashMap>(); + subtreeBuildersFromField = new HashMap>(); + } + + /** + * Record the starting location of a single value for a field. + * + * @param fieldDescriptor the field + * @param location source code location information + */ + public Builder setLocation( + final FieldDescriptor fieldDescriptor, TextFormatParseLocation location) { + List fieldLocations = locationsFromField.get(fieldDescriptor); + if (fieldLocations == null) { + fieldLocations = new ArrayList(); + locationsFromField.put(fieldDescriptor, fieldLocations); + } + fieldLocations.add(location); + return this; + } + + /** + * Set for a sub message. + * + *

A new builder is created for a sub message. The builder that is returned is a new builder. + * The return is not the invoked {@code builder.getBuilderForSubMessageField}. + * + * @param fieldDescriptor the field whose value is the submessage + * @return a new Builder for the sub message + */ + public Builder getBuilderForSubMessageField(final FieldDescriptor fieldDescriptor) { + List submessageBuilders = subtreeBuildersFromField.get(fieldDescriptor); + if (submessageBuilders == null) { + submessageBuilders = new ArrayList(); + subtreeBuildersFromField.put(fieldDescriptor, submessageBuilders); + } + Builder subtreeBuilder = new Builder(); + submessageBuilders.add(subtreeBuilder); + return subtreeBuilder; + } + + /** + * Build the {@code TextFormatParseInfoTree}. + * + * @return the {@code TextFormatParseInfoTree} + */ + public TextFormatParseInfoTree build() { + return new TextFormatParseInfoTree(locationsFromField, subtreeBuildersFromField); + } + } +} diff --git a/java/core/src/main/java/com/google/protobuf/TextFormatParseLocation.java b/java/core/src/main/java/com/google/protobuf/TextFormatParseLocation.java new file mode 100644 index 000000000..cce286e10 --- /dev/null +++ b/java/core/src/main/java/com/google/protobuf/TextFormatParseLocation.java @@ -0,0 +1,104 @@ +// 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. + +package com.google.protobuf; + +import java.util.Arrays; + +/** + * A location in the source code. + * + *

A location is the starting line number and starting column number. + */ +public final class TextFormatParseLocation { + + /** + * The empty location. + */ + public static final TextFormatParseLocation EMPTY = new TextFormatParseLocation(-1, -1); + + /** + * Create a location. + * + * @param line the starting line number + * @param column the starting column number + * @return a {@code ParseLocation} + */ + static TextFormatParseLocation create(int line, int column) { + if (line == -1 && column == -1) { + return EMPTY; + } + if (line < 0 || column < 0) { + throw new IllegalArgumentException( + String.format("line and column values must be >= 0: line %d, column: %d", line, column)); + } + return new TextFormatParseLocation(line, column); + } + + private final int line; + private final int column; + + private TextFormatParseLocation(int line, int column) { + this.line = line; + this.column = column; + } + + public int getLine() { + return line; + } + + public int getColumn() { + return column; + } + + @Override + public String toString() { + return String.format("ParseLocation{line=%d, column=%d}", line, column); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof TextFormatParseLocation)) { + return false; + } + TextFormatParseLocation that = (TextFormatParseLocation) o; + return (this.line == that.getLine()) + && (this.column == that.getColumn()); + } + + @Override + public int hashCode() { + int[] values = {line, column}; + return Arrays.hashCode(values); + } +} diff --git a/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java index 7cd2250e1..c906420da 100644 --- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java +++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java @@ -76,6 +76,7 @@ public final class UnknownFieldSet implements MessageLite { public static UnknownFieldSet getDefaultInstance() { return defaultInstance; } + @Override public UnknownFieldSet getDefaultInstanceForType() { return defaultInstance; } @@ -126,6 +127,7 @@ public final class UnknownFieldSet implements MessageLite { } /** Serializes the set and writes it to {@code output}. */ + @Override public void writeTo(final CodedOutputStream output) throws IOException { for (final Map.Entry entry : fields.entrySet()) { entry.getValue().writeTo(entry.getKey(), output); @@ -146,6 +148,7 @@ public final class UnknownFieldSet implements MessageLite { * Serializes the message to a {@code ByteString} and returns it. This is * just a trivial wrapper around {@link #writeTo(CodedOutputStream)}. */ + @Override public ByteString toByteString() { try { final ByteString.CodedBuilder out = @@ -163,6 +166,7 @@ public final class UnknownFieldSet implements MessageLite { * Serializes the message to a {@code byte} array and returns it. This is * just a trivial wrapper around {@link #writeTo(CodedOutputStream)}. */ + @Override public byte[] toByteArray() { try { final byte[] result = new byte[getSerializedSize()]; @@ -181,12 +185,14 @@ public final class UnknownFieldSet implements MessageLite { * Serializes the message and writes it to {@code output}. This is just a * trivial wrapper around {@link #writeTo(CodedOutputStream)}. */ + @Override public void writeTo(final OutputStream output) throws IOException { final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); writeTo(codedOutput); codedOutput.flush(); } + @Override public void writeDelimitedTo(OutputStream output) throws IOException { final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); codedOutput.writeRawVarint32(getSerializedSize()); @@ -195,6 +201,7 @@ public final class UnknownFieldSet implements MessageLite { } /** Get the number of bytes required to encode this set. */ + @Override public int getSerializedSize() { int result = 0; for (final Map.Entry entry : fields.entrySet()) { @@ -228,6 +235,7 @@ public final class UnknownFieldSet implements MessageLite { return result; } + @Override public boolean isInitialized() { // UnknownFieldSets do not have required fields, so they are always // initialized. @@ -258,10 +266,12 @@ public final class UnknownFieldSet implements MessageLite { return newBuilder().mergeFrom(input).build(); } + @Override public Builder newBuilderForType() { return newBuilder(); } + @Override public Builder toBuilder() { return newBuilder().mergeFrom(this); } @@ -329,6 +339,7 @@ public final class UnknownFieldSet implements MessageLite { * in undefined behavior and can cause a {@code NullPointerException} to be * thrown. */ + @Override public UnknownFieldSet build() { getFieldBuilder(0); // Force lastField to be built. final UnknownFieldSet result; @@ -341,6 +352,7 @@ public final class UnknownFieldSet implements MessageLite { return result; } + @Override public UnknownFieldSet buildPartial() { // No required fields, so this is the same as build(). return build(); @@ -353,6 +365,7 @@ public final class UnknownFieldSet implements MessageLite { new UnknownFieldSet(fields)); } + @Override public UnknownFieldSet getDefaultInstanceForType() { return UnknownFieldSet.getDefaultInstance(); } @@ -364,6 +377,7 @@ public final class UnknownFieldSet implements MessageLite { } /** Reset the builder to an empty set. */ + @Override public Builder clear() { reinitialize(); return this; @@ -487,6 +501,7 @@ public final class UnknownFieldSet implements MessageLite { * Parse an entire message from {@code input} and merge its fields into * this set. */ + @Override public Builder mergeFrom(final CodedInputStream input) throws IOException { while (true) { final int tag = input.readTag(); @@ -536,8 +551,8 @@ public final class UnknownFieldSet implements MessageLite { * set being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. */ - public Builder mergeFrom(final ByteString data) - throws InvalidProtocolBufferException { + @Override + public Builder mergeFrom(final ByteString data) throws InvalidProtocolBufferException { try { final CodedInputStream input = data.newCodedInput(); mergeFrom(input); @@ -557,8 +572,8 @@ public final class UnknownFieldSet implements MessageLite { * set being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. */ - public Builder mergeFrom(final byte[] data) - throws InvalidProtocolBufferException { + @Override + public Builder mergeFrom(final byte[] data) throws InvalidProtocolBufferException { try { final CodedInputStream input = CodedInputStream.newInstance(data); mergeFrom(input); @@ -578,6 +593,7 @@ public final class UnknownFieldSet implements MessageLite { * set being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. */ + @Override public Builder mergeFrom(final InputStream input) throws IOException { final CodedInputStream codedInput = CodedInputStream.newInstance(input); mergeFrom(codedInput); @@ -585,8 +601,8 @@ public final class UnknownFieldSet implements MessageLite { return this; } - public boolean mergeDelimitedFrom(InputStream input) - throws IOException { + @Override + public boolean mergeDelimitedFrom(InputStream input) throws IOException { final int firstByte = input.read(); if (firstByte == -1) { return false; @@ -597,30 +613,29 @@ public final class UnknownFieldSet implements MessageLite { return true; } - public boolean mergeDelimitedFrom( - InputStream input, - ExtensionRegistryLite extensionRegistry) throws IOException { + @Override + public boolean mergeDelimitedFrom(InputStream input, ExtensionRegistryLite extensionRegistry) + throws IOException { // UnknownFieldSet has no extensions. return mergeDelimitedFrom(input); } - public Builder mergeFrom( - CodedInputStream input, - ExtensionRegistryLite extensionRegistry) throws IOException { + @Override + public Builder mergeFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws IOException { // UnknownFieldSet has no extensions. return mergeFrom(input); } - public Builder mergeFrom( - ByteString data, - ExtensionRegistryLite extensionRegistry) + @Override + public Builder mergeFrom(ByteString data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { // UnknownFieldSet has no extensions. return mergeFrom(data); } - public Builder mergeFrom(byte[] data, int off, int len) - throws InvalidProtocolBufferException { + @Override + public Builder mergeFrom(byte[] data, int off, int len) throws InvalidProtocolBufferException { try { final CodedInputStream input = CodedInputStream.newInstance(data, off, len); @@ -636,29 +651,37 @@ public final class UnknownFieldSet implements MessageLite { } } - public Builder mergeFrom( - byte[] data, - ExtensionRegistryLite extensionRegistry) + @Override + public Builder mergeFrom(byte[] data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { // UnknownFieldSet has no extensions. return mergeFrom(data); } - public Builder mergeFrom( - byte[] data, int off, int len, - ExtensionRegistryLite extensionRegistry) + @Override + public Builder mergeFrom(byte[] data, int off, int len, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { // UnknownFieldSet has no extensions. return mergeFrom(data, off, len); } - public Builder mergeFrom( - InputStream input, - ExtensionRegistryLite extensionRegistry) throws IOException { + @Override + public Builder mergeFrom(InputStream input, ExtensionRegistryLite extensionRegistry) + throws IOException { // UnknownFieldSet has no extensions. return mergeFrom(input); } + @Override + public Builder mergeFrom(MessageLite m) { + if (m instanceof UnknownFieldSet) { + return mergeFrom((UnknownFieldSet) m); + } + throw new IllegalArgumentException( + "mergeFrom(MessageLite) can only merge messages of the same type."); + } + + @Override public boolean isInitialized() { // UnknownFieldSets do not have required fields, so they are always // initialized. @@ -987,6 +1010,7 @@ public final class UnknownFieldSet implements MessageLite { * Parser to implement MessageLite interface. */ public static final class Parser extends AbstractParser { + @Override public UnknownFieldSet parsePartialFrom( CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { @@ -1004,6 +1028,7 @@ public final class UnknownFieldSet implements MessageLite { } private static final Parser PARSER = new Parser(); + @Override public final Parser getParserForType() { return PARSER; } diff --git a/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java index 435ad4d42..9500f905f 100644 --- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java +++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java @@ -61,15 +61,6 @@ public final class UnknownFieldSetLite { public static UnknownFieldSetLite getDefaultInstance() { return DEFAULT_INSTANCE; } - - /** - * Returns an empty {@code UnknownFieldSetLite.Builder}. - * - *

For use by generated code only. - */ - public static Builder newBuilder() { - return new Builder(); - } /** * Returns a new mutable instance. @@ -262,6 +253,21 @@ public final class UnknownFieldSetLite { return hashCode; } + /** + * Prints a String representation of the unknown field set. + * + *

For use by generated code only. + * + * @param buffer the buffer to write to + * @param indent the number of spaces the fields should be indented by + */ + final void printWithIndent(StringBuilder buffer, int indent) { + for (int i = 0; i < count; i++) { + int fieldNumber = WireFormat.getTagFieldNumber(tags[i]); + MessageLiteToString.printField(buffer, indent, String.valueOf(fieldNumber), objects[i]); + } + } + private void storeField(int tag, Object value) { ensureCapacity(); @@ -369,90 +375,4 @@ public final class UnknownFieldSetLite { } return this; } - - /** - * Builder for {@link UnknownFieldSetLite}s. - * - *

Use {@link UnknownFieldSet#newBuilder()} to construct a {@code Builder}. - * - *

For use by generated code only. - */ - // TODO(dweis): Update the mutable API to no longer need this builder and delete. - public static final class Builder { - - private UnknownFieldSetLite set; - - private Builder() { - this.set = null; - } - - /** - * Ensures internal state is initialized for use. - */ - private void ensureNotBuilt() { - if (set == null) { - set = new UnknownFieldSetLite(); - } - - set.checkMutable(); - } - - /** - * Parse a single field from {@code input} and merge it into this set. - * - *

For use by generated code only. - * - * @param tag The field's tag number, which was already parsed. - * @return {@code false} if the tag is an end group tag. - */ - boolean mergeFieldFrom(final int tag, final CodedInputStream input) throws IOException { - ensureNotBuilt(); - return set.mergeFieldFrom(tag, input); - } - - /** - * Convenience method for merging a new field containing a single varint - * value. This is used in particular when an unknown enum value is - * encountered. - * - *

For use by generated code only. - */ - Builder mergeVarintField(int fieldNumber, int value) { - ensureNotBuilt(); - set.mergeVarintField(fieldNumber, value); - return this; - } - - /** - * Convenience method for merging a length-delimited field. - * - *

For use by generated code only. - */ - public Builder mergeLengthDelimitedField(final int fieldNumber, final ByteString value) { - ensureNotBuilt(); - set.mergeLengthDelimitedField(fieldNumber, value); - return this; - } - - /** - * Build the {@link UnknownFieldSetLite} and return it. - * - *

Once {@code build()} has been called, the {@code Builder} will no - * longer be usable. Calling any method after {@code build()} will result - * in undefined behavior and can cause an - * {@code UnsupportedOperationException} to be thrown. - * - *

For use by generated code only. - */ - public UnknownFieldSetLite build() { - if (set == null) { - return DEFAULT_INSTANCE; - } - - set.checkMutable(); - set.makeImmutable(); - - return set; - } - } } diff --git a/java/core/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java b/java/core/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java index 5257c5a23..30e879117 100644 --- a/java/core/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java +++ b/java/core/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java @@ -68,42 +68,42 @@ public class UnmodifiableLazyStringList extends AbstractList return list.size(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public ByteString getByteString(int index) { return list.getByteString(index); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void add(ByteString element) { throw new UnsupportedOperationException(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void set(int index, ByteString element) { throw new UnsupportedOperationException(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public boolean addAllByteString(Collection element) { throw new UnsupportedOperationException(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public byte[] getByteArray(int index) { return list.getByteArray(index); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void add(byte[] element) { throw new UnsupportedOperationException(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void set(int index, byte[] element) { throw new UnsupportedOperationException(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public boolean addAllByteArray(Collection element) { throw new UnsupportedOperationException(); } @@ -113,47 +113,47 @@ public class UnmodifiableLazyStringList extends AbstractList return new ListIterator() { ListIterator iter = list.listIterator(index); - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public boolean hasNext() { return iter.hasNext(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public String next() { return iter.next(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public boolean hasPrevious() { return iter.hasPrevious(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public String previous() { return iter.previous(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public int nextIndex() { return iter.nextIndex(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public int previousIndex() { return iter.previousIndex(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void remove() { throw new UnsupportedOperationException(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void set(String o) { throw new UnsupportedOperationException(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void add(String o) { throw new UnsupportedOperationException(); } @@ -165,45 +165,45 @@ public class UnmodifiableLazyStringList extends AbstractList return new Iterator() { Iterator iter = list.iterator(); - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public boolean hasNext() { return iter.hasNext(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public String next() { return iter.next(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void remove() { throw new UnsupportedOperationException(); } }; } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public List getUnderlyingElements() { // The returned value is already unmodifiable. return list.getUnderlyingElements(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void mergeFrom(LazyStringList other) { throw new UnsupportedOperationException(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public List asByteArrayList() { return Collections.unmodifiableList(list.asByteArrayList()); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public List asByteStringList() { return Collections.unmodifiableList(list.asByteStringList()); } - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public LazyStringList getUnmodifiableView() { return this; } diff --git a/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java b/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java index f443ee39f..0fbf4d401 100644 --- a/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java +++ b/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java @@ -30,6 +30,7 @@ package com.google.protobuf; +import java.io.IOException; import java.nio.ByteBuffer; /** @@ -49,8 +50,8 @@ public final class UnsafeByteOperations { /** * An unsafe operation that returns a {@link ByteString} that is backed by the provided buffer. * - * @param buffer the Java NIO buffer to be wrapped. - * @return a {@link ByteString} backed by the provided buffer. + * @param buffer the Java NIO buffer to be wrapped + * @return a {@link ByteString} backed by the provided buffer */ public static ByteString unsafeWrap(ByteBuffer buffer) { if (buffer.hasArray()) { @@ -60,4 +61,24 @@ public final class UnsafeByteOperations { return new NioByteString(buffer); } } + + /** + * Writes the given {@link ByteString} to the provided {@link ByteOutput}. Calling this method may + * result in multiple operations on the target {@link ByteOutput} + * (i.e. for roped {@link ByteString}s). + * + *

This method exposes the internal backing buffer(s) of the {@link ByteString} to the {@link + * ByteOutput} in order to avoid additional copying overhead. It would be possible for a malicious + * {@link ByteOutput} to corrupt the {@link ByteString}. Use with caution! + * + *

NOTE: The {@link ByteOutput} MUST NOT modify the provided buffers. Doing + * so may result in corrupted data, which would be difficult to debug. + * + * @param bytes the {@link ByteString} to be written + * @param output the output to receive the bytes + * @throws IOException if an I/O error occurs + */ + public static void unsafeWriteTo(ByteString bytes, ByteOutput output) throws IOException { + bytes.writeTo(output); + } } diff --git a/java/core/src/main/java/com/google/protobuf/Utf8.java b/java/core/src/main/java/com/google/protobuf/Utf8.java index 48c7e9e62..308c69e95 100644 --- a/java/core/src/main/java/com/google/protobuf/Utf8.java +++ b/java/core/src/main/java/com/google/protobuf/Utf8.java @@ -30,6 +30,19 @@ package com.google.protobuf; +import static java.lang.Character.MAX_SURROGATE; +import static java.lang.Character.MIN_SURROGATE; +import static java.lang.Character.isSurrogatePair; +import static java.lang.Character.toCodePoint; + +import java.lang.reflect.Field; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.util.logging.Level; +import java.util.logging.Logger; + /** * A set of low-level, high-performance static utility methods related * to the UTF-8 character encoding. This class has no dependencies @@ -64,9 +77,24 @@ package com.google.protobuf; * * @author martinrb@google.com (Martin Buchholz) */ +// TODO(nathanmittler): Copy changes in this class back to Guava final class Utf8 { - private Utf8() {} - + private static final Logger logger = Logger.getLogger(Utf8.class.getName()); + + /** + * UTF-8 is a runtime hot spot so we attempt to provide heavily optimized implementations + * depending on what is available on the platform. The processor is the platform-optimized + * delegate for which all methods are delegated directly to. + */ + private static final Processor processor = + UnsafeProcessor.isAvailable() ? new UnsafeProcessor() : new SafeProcessor(); + + /** + * A mask used when performing unsafe reads to determine if a long value contains any non-ASCII + * characters (i.e. any byte >= 0x80). + */ + private static final long ASCII_MASK_LONG = 0x8080808080808080L; + /** * Maximum number of bytes per Java UTF-16 char in UTF-8. * @see java.nio.charset.CharsetEncoder#maxBytesPerChar() @@ -85,6 +113,18 @@ final class Utf8 { */ public static final int MALFORMED = -1; + /** + * Used by {@code Unsafe} UTF-8 string validation logic to determine the minimum string length + * above which to employ an optimized algorithm for counting ASCII characters. The reason for this + * threshold is that for small strings, the optimization may not be beneficial or may even + * negatively impact performance since it requires additional logic to avoid unaligned reads + * (when calling {@code Unsafe.getLong}). This threshold guarantees that even if the initial + * offset is unaligned, we're guaranteed to make at least one call to {@code Unsafe.getLong()} + * which provides a performance improvement that entirely subsumes the cost of the additional + * logic. + */ + private static final int UNSAFE_COUNT_ASCII_THRESHOLD = 16; + // Other state values include the partial bytes of the incomplete // character to be decoded in the simplest way: we pack the bytes // into the state int in little-endian order. For example: @@ -112,7 +152,7 @@ final class Utf8 { * isValidUtf8(bytes, 0, bytes.length)}. */ public static boolean isValidUtf8(byte[] bytes) { - return isValidUtf8(bytes, 0, bytes.length); + return processor.isValidUtf8(bytes, 0, bytes.length); } /** @@ -125,7 +165,7 @@ final class Utf8 { * partialIsValidUtf8(bytes, index, limit) == Utf8.COMPLETE}. */ public static boolean isValidUtf8(byte[] bytes, int index, int limit) { - return partialIsValidUtf8(bytes, index, limit) == COMPLETE; + return processor.isValidUtf8(bytes, index, limit); } /** @@ -146,183 +186,8 @@ final class Utf8 { * decode the character when passed to a subsequent invocation of a * partial decoding method. */ - public static int partialIsValidUtf8( - int state, byte[] bytes, int index, int limit) { - if (state != COMPLETE) { - // The previous decoding operation was incomplete (or malformed). - // We look for a well-formed sequence consisting of bytes from - // the previous decoding operation (stored in state) together - // with bytes from the array slice. - // - // We expect such "straddler characters" to be rare. - - if (index >= limit) { // No bytes? No progress. - return state; - } - int byte1 = (byte) state; - // byte1 is never ASCII. - if (byte1 < (byte) 0xE0) { - // two-byte form - - // Simultaneously checks for illegal trailing-byte in - // leading position and overlong 2-byte form. - if (byte1 < (byte) 0xC2 || - // byte2 trailing-byte test - bytes[index++] > (byte) 0xBF) { - return MALFORMED; - } - } else if (byte1 < (byte) 0xF0) { - // three-byte form - - // Get byte2 from saved state or array - int byte2 = (byte) ~(state >> 8); - if (byte2 == 0) { - byte2 = bytes[index++]; - if (index >= limit) { - return incompleteStateFor(byte1, byte2); - } - } - if (byte2 > (byte) 0xBF || - // overlong? 5 most significant bits must not all be zero - (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) || - // illegal surrogate codepoint? - (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) || - // byte3 trailing-byte test - bytes[index++] > (byte) 0xBF) { - return MALFORMED; - } - } else { - // four-byte form - - // Get byte2 and byte3 from saved state or array - int byte2 = (byte) ~(state >> 8); - int byte3 = 0; - if (byte2 == 0) { - byte2 = bytes[index++]; - if (index >= limit) { - return incompleteStateFor(byte1, byte2); - } - } else { - byte3 = (byte) (state >> 16); - } - if (byte3 == 0) { - byte3 = bytes[index++]; - if (index >= limit) { - return incompleteStateFor(byte1, byte2, byte3); - } - } - - // If we were called with state == MALFORMED, then byte1 is 0xFF, - // which never occurs in well-formed UTF-8, and so we will return - // MALFORMED again below. - - if (byte2 > (byte) 0xBF || - // Check that 1 <= plane <= 16. Tricky optimized form of: - // if (byte1 > (byte) 0xF4 || - // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || - // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) - (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 || - // byte3 trailing-byte test - byte3 > (byte) 0xBF || - // byte4 trailing-byte test - bytes[index++] > (byte) 0xBF) { - return MALFORMED; - } - } - } - - return partialIsValidUtf8(bytes, index, limit); - } - - /** - * Tells whether the given byte array slice is a well-formed, - * malformed, or incomplete UTF-8 byte sequence. The range of bytes - * to be checked extends from index {@code index}, inclusive, to - * {@code limit}, exclusive. - * - *

This is a convenience method, equivalent to a call to {@code - * partialIsValidUtf8(Utf8.COMPLETE, bytes, index, limit)}. - * - * @return {@link #MALFORMED} if the partial byte sequence is - * definitely not well-formed, {@link #COMPLETE} if it is well-formed - * (no additional input needed), or if the byte sequence is - * "incomplete", i.e. apparently terminated in the middle of a character, - * an opaque integer "state" value containing enough information to - * decode the character when passed to a subsequent invocation of a - * partial decoding method. - */ - public static int partialIsValidUtf8( - byte[] bytes, int index, int limit) { - // Optimize for 100% ASCII. - // Hotspot loves small simple top-level loops like this. - while (index < limit && bytes[index] >= 0) { - index++; - } - - return (index >= limit) ? COMPLETE : - partialIsValidUtf8NonAscii(bytes, index, limit); - } - - private static int partialIsValidUtf8NonAscii( - byte[] bytes, int index, int limit) { - for (;;) { - int byte1, byte2; - - // Optimize for interior runs of ASCII bytes. - do { - if (index >= limit) { - return COMPLETE; - } - } while ((byte1 = bytes[index++]) >= 0); - - if (byte1 < (byte) 0xE0) { - // two-byte form - - if (index >= limit) { - return byte1; - } - - // Simultaneously checks for illegal trailing-byte in - // leading position and overlong 2-byte form. - if (byte1 < (byte) 0xC2 || - bytes[index++] > (byte) 0xBF) { - return MALFORMED; - } - } else if (byte1 < (byte) 0xF0) { - // three-byte form - - if (index >= limit - 1) { // incomplete sequence - return incompleteStateFor(bytes, index, limit); - } - if ((byte2 = bytes[index++]) > (byte) 0xBF || - // overlong? 5 most significant bits must not all be zero - (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) || - // check for illegal surrogate codepoints - (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) || - // byte3 trailing-byte test - bytes[index++] > (byte) 0xBF) { - return MALFORMED; - } - } else { - // four-byte form - - if (index >= limit - 2) { // incomplete sequence - return incompleteStateFor(bytes, index, limit); - } - if ((byte2 = bytes[index++]) > (byte) 0xBF || - // Check that 1 <= plane <= 16. Tricky optimized form of: - // if (byte1 > (byte) 0xF4 || - // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || - // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) - (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 || - // byte3 trailing-byte test - bytes[index++] > (byte) 0xBF || - // byte4 trailing-byte test - bytes[index++] > (byte) 0xBF) { - return MALFORMED; - } - } - } + public static int partialIsValidUtf8(int state, byte[] bytes, int index, int limit) { + return processor.partialIsValidUtf8(state, bytes, index, limit); } private static int incompleteStateFor(int byte1) { @@ -352,19 +217,31 @@ final class Utf8 { default: throw new AssertionError(); } } - + + private static int incompleteStateFor( + final ByteBuffer buffer, final int byte1, final int index, final int remaining) { + switch (remaining) { + case 0: + return incompleteStateFor(byte1); + case 1: + return incompleteStateFor(byte1, buffer.get(index)); + case 2: + return incompleteStateFor(byte1, buffer.get(index), buffer.get(index + 1)); + default: + throw new AssertionError(); + } + } // These UTF-8 handling methods are copied from Guava's Utf8 class with a modification to throw // a protocol buffer local exception. This exception is then caught in CodedOutputStream so it can // fallback to more lenient behavior. static class UnpairedSurrogateException extends IllegalArgumentException { - private UnpairedSurrogateException(int index, int length) { super("Unpaired surrogate at index " + index + " of " + length); } } - + /** * Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. For a string, * this method is equivalent to {@code string.getBytes(UTF_8).length}, but is more efficient in @@ -426,56 +303,1381 @@ final class Utf8 { return utf8Length; } - static int encode(CharSequence sequence, byte[] bytes, int offset, int length) { - int utf16Length = sequence.length(); - int j = offset; - int i = 0; - int limit = offset + length; - // Designed to take advantage of - // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination - for (char c; i < utf16Length && i + j < limit && (c = sequence.charAt(i)) < 0x80; i++) { - bytes[j + i] = (byte) c; - } - if (i == utf16Length) { - return j + utf16Length; - } - j += i; - for (char c; i < utf16Length; i++) { - c = sequence.charAt(i); - if (c < 0x80 && j < limit) { - bytes[j++] = (byte) c; - } else if (c < 0x800 && j <= limit - 2) { // 11 bits, two UTF-8 bytes - bytes[j++] = (byte) ((0xF << 6) | (c >>> 6)); - bytes[j++] = (byte) (0x80 | (0x3F & c)); - } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) && j <= limit - 3) { - // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes - bytes[j++] = (byte) ((0xF << 5) | (c >>> 12)); - bytes[j++] = (byte) (0x80 | (0x3F & (c >>> 6))); - bytes[j++] = (byte) (0x80 | (0x3F & c)); - } else if (j <= limit - 4) { - // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8 bytes - final char low; - if (i + 1 == sequence.length() - || !Character.isSurrogatePair(c, (low = sequence.charAt(++i)))) { - throw new UnpairedSurrogateException((i - 1), utf16Length); - } - int codePoint = Character.toCodePoint(c, low); - bytes[j++] = (byte) ((0xF << 4) | (codePoint >>> 18)); - bytes[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 12))); - bytes[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 6))); - bytes[j++] = (byte) (0x80 | (0x3F & codePoint)); - } else { - // If we are surrogates and we're not a surrogate pair, always throw an - // IllegalArgumentException instead of an ArrayOutOfBoundsException. - if ((Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE) - && (i + 1 == sequence.length() - || !Character.isSurrogatePair(c, sequence.charAt(i + 1)))) { - throw new UnpairedSurrogateException(i, utf16Length); - } - throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + j); - } - } - return j; + static int encode(CharSequence in, byte[] out, int offset, int length) { + return processor.encodeUtf8(in, out, offset, length); } // End Guava UTF-8 methods. + + /** + * Determines if the given {@link ByteBuffer} is a valid UTF-8 string. + * + *

Selects an optimal algorithm based on the type of {@link ByteBuffer} (i.e. heap or direct) + * and the capabilities of the platform. + * + * @param buffer the buffer to check. + * @see Utf8#isValidUtf8(byte[], int, int) + */ + static boolean isValidUtf8(ByteBuffer buffer) { + return processor.isValidUtf8(buffer, buffer.position(), buffer.remaining()); + } + + /** + * Determines if the given {@link ByteBuffer} is a partially valid UTF-8 string. + * + *

Selects an optimal algorithm based on the type of {@link ByteBuffer} (i.e. heap or direct) + * and the capabilities of the platform. + * + * @param buffer the buffer to check. + * @see Utf8#partialIsValidUtf8(int, byte[], int, int) + */ + static int partialIsValidUtf8(int state, ByteBuffer buffer, int index, int limit) { + return processor.partialIsValidUtf8(state, buffer, index, limit); + } + + /** + * Encodes the given characters to the target {@link ByteBuffer} using UTF-8 encoding. + * + *

Selects an optimal algorithm based on the type of {@link ByteBuffer} (i.e. heap or direct) + * and the capabilities of the platform. + * + * @param in the source string to be encoded + * @param out the target buffer to receive the encoded string. + * @see Utf8#encode(CharSequence, byte[], int, int) + */ + static void encodeUtf8(CharSequence in, ByteBuffer out) { + processor.encodeUtf8(in, out); + } + + /** + * Counts (approximately) the number of consecutive ASCII characters in the given buffer. + * The byte order of the {@link ByteBuffer} does not matter, so performance can be improved if + * native byte order is used (i.e. no byte-swapping in {@link ByteBuffer#getLong(int)}). + * + * @param buffer the buffer to be scanned for ASCII chars + * @param index the starting index of the scan + * @param limit the limit within buffer for the scan + * @return the number of ASCII characters found. The stopping position will be at or + * before the first non-ASCII byte. + */ + private static int estimateConsecutiveAscii(ByteBuffer buffer, int index, int limit) { + int i = index; + final int lim = limit - 7; + // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII). + // To speed things up further, we're reading longs instead of bytes so we use a mask to + // determine if any byte in the current long is non-ASCII. + for (; i < lim && (buffer.getLong(i) & ASCII_MASK_LONG) == 0; i += 8) {} + return i - index; + } + + /** + * A processor of UTF-8 strings, providing methods for checking validity and encoding. + */ + // TODO(nathanmittler): Add support for Memory/MemoryBlock on Android. + abstract static class Processor { + /** + * Returns {@code true} if the given byte array slice is a + * well-formed UTF-8 byte sequence. The range of bytes to be + * checked extends from index {@code index}, inclusive, to {@code + * limit}, exclusive. + * + *

This is a convenience method, equivalent to {@code + * partialIsValidUtf8(bytes, index, limit) == Utf8.COMPLETE}. + */ + final boolean isValidUtf8(byte[] bytes, int index, int limit) { + return partialIsValidUtf8(COMPLETE, bytes, index, limit) == COMPLETE; + } + + /** + * Tells whether the given byte array slice is a well-formed, + * malformed, or incomplete UTF-8 byte sequence. The range of bytes + * to be checked extends from index {@code index}, inclusive, to + * {@code limit}, exclusive. + * + * @param state either {@link Utf8#COMPLETE} (if this is the initial decoding + * operation) or the value returned from a call to a partial decoding method + * for the previous bytes + * + * @return {@link #MALFORMED} if the partial byte sequence is + * definitely not well-formed, {@link #COMPLETE} if it is well-formed + * (no additional input needed), or if the byte sequence is + * "incomplete", i.e. apparently terminated in the middle of a character, + * an opaque integer "state" value containing enough information to + * decode the character when passed to a subsequent invocation of a + * partial decoding method. + */ + abstract int partialIsValidUtf8(int state, byte[] bytes, int index, int limit); + + /** + * Returns {@code true} if the given portion of the {@link ByteBuffer} is a + * well-formed UTF-8 byte sequence. The range of bytes to be + * checked extends from index {@code index}, inclusive, to {@code + * limit}, exclusive. + * + *

This is a convenience method, equivalent to {@code + * partialIsValidUtf8(bytes, index, limit) == Utf8.COMPLETE}. + */ + final boolean isValidUtf8(ByteBuffer buffer, int index, int limit) { + return partialIsValidUtf8(COMPLETE, buffer, index, limit) == COMPLETE; + } + + /** + * Indicates whether or not the given buffer contains a valid UTF-8 string. + * + * @param buffer the buffer to check. + * @return {@code true} if the given buffer contains a valid UTF-8 string. + */ + final int partialIsValidUtf8( + final int state, final ByteBuffer buffer, int index, final int limit) { + if (buffer.hasArray()) { + final int offset = buffer.arrayOffset(); + return partialIsValidUtf8(state, buffer.array(), offset + index, offset + limit); + } else if (buffer.isDirect()){ + return partialIsValidUtf8Direct(state, buffer, index, limit); + } + return partialIsValidUtf8Default(state, buffer, index, limit); + } + + /** + * Performs validation for direct {@link ByteBuffer} instances. + */ + abstract int partialIsValidUtf8Direct( + final int state, final ByteBuffer buffer, int index, final int limit); + + /** + * Performs validation for {@link ByteBuffer} instances using the {@link ByteBuffer} API rather + * than potentially faster approaches. This first completes validation for the current + * character (provided by {@code state}) and then finishes validation for the sequence. + */ + final int partialIsValidUtf8Default( + final int state, final ByteBuffer buffer, int index, final int limit) { + if (state != COMPLETE) { + // The previous decoding operation was incomplete (or malformed). + // We look for a well-formed sequence consisting of bytes from + // the previous decoding operation (stored in state) together + // with bytes from the array slice. + // + // We expect such "straddler characters" to be rare. + + if (index >= limit) { // No bytes? No progress. + return state; + } + + byte byte1 = (byte) state; + // byte1 is never ASCII. + if (byte1 < (byte) 0xE0) { + // two-byte form + + // Simultaneously checks for illegal trailing-byte in + // leading position and overlong 2-byte form. + if (byte1 < (byte) 0xC2 + // byte2 trailing-byte test + || buffer.get(index++) > (byte) 0xBF) { + return MALFORMED; + } + } else if (byte1 < (byte) 0xF0) { + // three-byte form + + // Get byte2 from saved state or array + byte byte2 = (byte) ~(state >> 8); + if (byte2 == 0) { + byte2 = buffer.get(index++); + if (index >= limit) { + return incompleteStateFor(byte1, byte2); + } + } + if (byte2 > (byte) 0xBF + // overlong? 5 most significant bits must not all be zero + || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) + // illegal surrogate codepoint? + || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) + // byte3 trailing-byte test + || buffer.get(index++) > (byte) 0xBF) { + return MALFORMED; + } + } else { + // four-byte form + + // Get byte2 and byte3 from saved state or array + byte byte2 = (byte) ~(state >> 8); + byte byte3 = 0; + if (byte2 == 0) { + byte2 = buffer.get(index++); + if (index >= limit) { + return incompleteStateFor(byte1, byte2); + } + } else { + byte3 = (byte) (state >> 16); + } + if (byte3 == 0) { + byte3 = buffer.get(index++); + if (index >= limit) { + return incompleteStateFor(byte1, byte2, byte3); + } + } + + // If we were called with state == MALFORMED, then byte1 is 0xFF, + // which never occurs in well-formed UTF-8, and so we will return + // MALFORMED again below. + + if (byte2 > (byte) 0xBF + // Check that 1 <= plane <= 16. Tricky optimized form of: + // if (byte1 > (byte) 0xF4 || + // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || + // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) + || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 + // byte3 trailing-byte test + || byte3 > (byte) 0xBF + // byte4 trailing-byte test + || buffer.get(index++) > (byte) 0xBF) { + return MALFORMED; + } + } + } + + // Finish validation for the sequence. + return partialIsValidUtf8(buffer, index, limit); + } + + /** + * Performs validation for {@link ByteBuffer} instances using the {@link ByteBuffer} API rather + * than potentially faster approaches. + */ + private static int partialIsValidUtf8(final ByteBuffer buffer, int index, final int limit) { + index += estimateConsecutiveAscii(buffer, index, limit); + + for (;;) { + // Optimize for interior runs of ASCII bytes. + // TODO(nathanmittler): Consider checking 8 bytes at a time after some threshold? + // Maybe after seeing a few in a row that are ASCII, go back to fast mode? + int byte1; + do { + if (index >= limit) { + return COMPLETE; + } + } while ((byte1 = buffer.get(index++)) >= 0); + + // If we're here byte1 is not ASCII. Only need to handle 2-4 byte forms. + if (byte1 < (byte) 0xE0) { + // Two-byte form (110xxxxx 10xxxxxx) + if (index >= limit) { + // Incomplete sequence + return byte1; + } + + // Simultaneously checks for illegal trailing-byte in + // leading position and overlong 2-byte form. + if (byte1 < (byte) 0xC2 || buffer.get(index) > (byte) 0xBF) { + return MALFORMED; + } + index++; + } else if (byte1 < (byte) 0xF0) { + // Three-byte form (1110xxxx 10xxxxxx 10xxxxxx) + if (index >= limit - 1) { + // Incomplete sequence + return incompleteStateFor(buffer, byte1, index, limit - index); + } + + final byte byte2 = buffer.get(index++); + if (byte2 > (byte) 0xBF + // overlong? 5 most significant bits must not all be zero + || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) + // check for illegal surrogate codepoints + || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) + // byte3 trailing-byte test + || buffer.get(index) > (byte) 0xBF) { + return MALFORMED; + } + index++; + } else { + // Four-byte form (1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx) + if (index >= limit - 2) { + // Incomplete sequence + return incompleteStateFor(buffer, byte1, index, limit - index); + } + + // TODO(nathanmittler): Consider using getInt() to improve performance. + final int byte2 = buffer.get(index++); + if (byte2 > (byte) 0xBF + // Check that 1 <= plane <= 16. Tricky optimized form of: + // if (byte1 > (byte) 0xF4 || + // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || + // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) + || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 + // byte3 trailing-byte test + || buffer.get(index++) > (byte) 0xBF + // byte4 trailing-byte test + || buffer.get(index++) > (byte) 0xBF) { + return MALFORMED; + } + } + } + } + + /** + * Encodes an input character sequence ({@code in}) to UTF-8 in the target array ({@code out}). + * For a string, this method is similar to + *

{@code
+     * byte[] a = string.getBytes(UTF_8);
+     * System.arraycopy(a, 0, bytes, offset, a.length);
+     * return offset + a.length;
+     * }
+ * + * but is more efficient in both time and space. One key difference is that this method + * requires paired surrogates, and therefore does not support chunking. + * While {@code String.getBytes(UTF_8)} replaces unpaired surrogates with the default + * replacement character, this method throws {@link UnpairedSurrogateException}. + * + *

To ensure sufficient space in the output buffer, either call {@link #encodedLength} to + * compute the exact amount needed, or leave room for + * {@code Utf8.MAX_BYTES_PER_CHAR * sequence.length()}, which is the largest possible number + * of bytes that any input can be encoded to. + * + * @param in the input character sequence to be encoded + * @param out the target array + * @param offset the starting offset in {@code bytes} to start writing at + * @param length the length of the {@code bytes}, starting from {@code offset} + * @throws UnpairedSurrogateException if {@code sequence} contains ill-formed UTF-16 (unpaired + * surrogates) + * @throws ArrayIndexOutOfBoundsException if {@code sequence} encoded in UTF-8 is longer than + * {@code bytes.length - offset} + * @return the new offset, equivalent to {@code offset + Utf8.encodedLength(sequence)} + */ + abstract int encodeUtf8(CharSequence in, byte[] out, int offset, int length); + + /** + * Encodes an input character sequence ({@code in}) to UTF-8 in the target buffer ({@code out}). + * Upon returning from this method, the {@code out} position will point to the position after + * the last encoded byte. This method requires paired surrogates, and therefore does not + * support chunking. + * + *

To ensure sufficient space in the output buffer, either call {@link #encodedLength} to + * compute the exact amount needed, or leave room for + * {@code Utf8.MAX_BYTES_PER_CHAR * in.length()}, which is the largest possible number + * of bytes that any input can be encoded to. + * + * @param in the source character sequence to be encoded + * @param out the target buffer + * @throws UnpairedSurrogateException if {@code in} contains ill-formed UTF-16 (unpaired + * surrogates) + * @throws ArrayIndexOutOfBoundsException if {@code in} encoded in UTF-8 is longer than + * {@code out.remaining()} + */ + final void encodeUtf8(CharSequence in, ByteBuffer out) { + if (out.hasArray()) { + final int offset = out.arrayOffset(); + int endIndex = + Utf8.encode(in, out.array(), offset + out.position(), out.remaining()); + out.position(endIndex - offset); + } else if (out.isDirect()) { + encodeUtf8Direct(in, out); + } else { + encodeUtf8Default(in, out); + } + } + + /** + * Encodes the input character sequence to a direct {@link ByteBuffer} instance. + */ + abstract void encodeUtf8Direct(CharSequence in, ByteBuffer out); + + /** + * Encodes the input character sequence to a {@link ByteBuffer} instance using the {@link + * ByteBuffer} API, rather than potentially faster approaches. + */ + final void encodeUtf8Default(CharSequence in, ByteBuffer out) { + final int inLength = in.length(); + int outIx = out.position(); + int inIx = 0; + + // Since ByteBuffer.putXXX() already checks boundaries for us, no need to explicitly check + // access. Assume the buffer is big enough and let it handle the out of bounds exception + // if it occurs. + try { + // Designed to take advantage of + // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination + for (char c; inIx < inLength && (c = in.charAt(inIx)) < 0x80; ++inIx) { + out.put(outIx + inIx, (byte) c); + } + if (inIx == inLength) { + // Successfully encoded the entire string. + out.position(outIx + inIx); + return; + } + + outIx += inIx; + for (char c; inIx < inLength; ++inIx, ++outIx) { + c = in.charAt(inIx); + if (c < 0x80) { + // One byte (0xxx xxxx) + out.put(outIx, (byte) c); + } else if (c < 0x800) { + // Two bytes (110x xxxx 10xx xxxx) + + // Benchmarks show put performs better than putShort here (for HotSpot). + out.put(outIx++, (byte) (0xC0 | (c >>> 6))); + out.put(outIx, (byte) (0x80 | (0x3F & c))); + } else if (c < MIN_SURROGATE || MAX_SURROGATE < c) { + // Three bytes (1110 xxxx 10xx xxxx 10xx xxxx) + // Maximum single-char code point is 0xFFFF, 16 bits. + + // Benchmarks show put performs better than putShort here (for HotSpot). + out.put(outIx++, (byte) (0xE0 | (c >>> 12))); + out.put(outIx++, (byte) (0x80 | (0x3F & (c >>> 6)))); + out.put(outIx, (byte) (0x80 | (0x3F & c))); + } else { + // Four bytes (1111 xxxx 10xx xxxx 10xx xxxx 10xx xxxx) + + // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8 + // bytes + final char low; + if (inIx + 1 == inLength || !isSurrogatePair(c, (low = in.charAt(++inIx)))) { + throw new UnpairedSurrogateException(inIx, inLength); + } + // TODO(nathanmittler): Consider using putInt() to improve performance. + int codePoint = toCodePoint(c, low); + out.put(outIx++, (byte) ((0xF << 4) | (codePoint >>> 18))); + out.put(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12)))); + out.put(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6)))); + out.put(outIx, (byte) (0x80 | (0x3F & codePoint))); + } + } + + // Successfully encoded the entire string. + out.position(outIx); + } catch (IndexOutOfBoundsException e) { + // TODO(nathanmittler): Consider making the API throw IndexOutOfBoundsException instead. + + // If we failed in the outer ASCII loop, outIx will not have been updated. In this case, + // use inIx to determine the bad write index. + int badWriteIndex = out.position() + Math.max(inIx, outIx - out.position() + 1); + throw new ArrayIndexOutOfBoundsException( + "Failed writing " + in.charAt(inIx) + " at index " + badWriteIndex); + } + } + } + + /** + * {@link Processor} implementation that does not use any {@code sun.misc.Unsafe} methods. + */ + static final class SafeProcessor extends Processor { + @Override + int partialIsValidUtf8(int state, byte[] bytes, int index, int limit) { + if (state != COMPLETE) { + // The previous decoding operation was incomplete (or malformed). + // We look for a well-formed sequence consisting of bytes from + // the previous decoding operation (stored in state) together + // with bytes from the array slice. + // + // We expect such "straddler characters" to be rare. + + if (index >= limit) { // No bytes? No progress. + return state; + } + int byte1 = (byte) state; + // byte1 is never ASCII. + if (byte1 < (byte) 0xE0) { + // two-byte form + + // Simultaneously checks for illegal trailing-byte in + // leading position and overlong 2-byte form. + if (byte1 < (byte) 0xC2 + // byte2 trailing-byte test + || bytes[index++] > (byte) 0xBF) { + return MALFORMED; + } + } else if (byte1 < (byte) 0xF0) { + // three-byte form + + // Get byte2 from saved state or array + int byte2 = (byte) ~(state >> 8); + if (byte2 == 0) { + byte2 = bytes[index++]; + if (index >= limit) { + return incompleteStateFor(byte1, byte2); + } + } + if (byte2 > (byte) 0xBF + // overlong? 5 most significant bits must not all be zero + || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) + // illegal surrogate codepoint? + || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) + // byte3 trailing-byte test + || bytes[index++] > (byte) 0xBF) { + return MALFORMED; + } + } else { + // four-byte form + + // Get byte2 and byte3 from saved state or array + int byte2 = (byte) ~(state >> 8); + int byte3 = 0; + if (byte2 == 0) { + byte2 = bytes[index++]; + if (index >= limit) { + return incompleteStateFor(byte1, byte2); + } + } else { + byte3 = (byte) (state >> 16); + } + if (byte3 == 0) { + byte3 = bytes[index++]; + if (index >= limit) { + return incompleteStateFor(byte1, byte2, byte3); + } + } + + // If we were called with state == MALFORMED, then byte1 is 0xFF, + // which never occurs in well-formed UTF-8, and so we will return + // MALFORMED again below. + + if (byte2 > (byte) 0xBF + // Check that 1 <= plane <= 16. Tricky optimized form of: + // if (byte1 > (byte) 0xF4 || + // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || + // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) + || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 + // byte3 trailing-byte test + || byte3 > (byte) 0xBF + // byte4 trailing-byte test + || bytes[index++] > (byte) 0xBF) { + return MALFORMED; + } + } + } + + return partialIsValidUtf8(bytes, index, limit); + } + + @Override + int partialIsValidUtf8Direct(int state, ByteBuffer buffer, int index, int limit) { + // For safe processing, we have to use the ByteBuffer API. + return partialIsValidUtf8Default(state, buffer, index, limit); + } + + @Override + int encodeUtf8(CharSequence in, byte[] out, int offset, int length) { + int utf16Length = in.length(); + int j = offset; + int i = 0; + int limit = offset + length; + // Designed to take advantage of + // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination + for (char c; i < utf16Length && i + j < limit && (c = in.charAt(i)) < 0x80; i++) { + out[j + i] = (byte) c; + } + if (i == utf16Length) { + return j + utf16Length; + } + j += i; + for (char c; i < utf16Length; i++) { + c = in.charAt(i); + if (c < 0x80 && j < limit) { + out[j++] = (byte) c; + } else if (c < 0x800 && j <= limit - 2) { // 11 bits, two UTF-8 bytes + out[j++] = (byte) ((0xF << 6) | (c >>> 6)); + out[j++] = (byte) (0x80 | (0x3F & c)); + } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) && j <= limit - 3) { + // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes + out[j++] = (byte) ((0xF << 5) | (c >>> 12)); + out[j++] = (byte) (0x80 | (0x3F & (c >>> 6))); + out[j++] = (byte) (0x80 | (0x3F & c)); + } else if (j <= limit - 4) { + // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, + // four UTF-8 bytes + final char low; + if (i + 1 == in.length() + || !Character.isSurrogatePair(c, (low = in.charAt(++i)))) { + throw new UnpairedSurrogateException((i - 1), utf16Length); + } + int codePoint = Character.toCodePoint(c, low); + out[j++] = (byte) ((0xF << 4) | (codePoint >>> 18)); + out[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 12))); + out[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 6))); + out[j++] = (byte) (0x80 | (0x3F & codePoint)); + } else { + // If we are surrogates and we're not a surrogate pair, always throw an + // UnpairedSurrogateException instead of an ArrayOutOfBoundsException. + if ((Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE) + && (i + 1 == in.length() + || !Character.isSurrogatePair(c, in.charAt(i + 1)))) { + throw new UnpairedSurrogateException(i, utf16Length); + } + throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + j); + } + } + return j; + } + + @Override + void encodeUtf8Direct(CharSequence in, ByteBuffer out) { + // For safe processing, we have to use the ByteBuffer API. + encodeUtf8Default(in, out); + } + + private static int partialIsValidUtf8(byte[] bytes, int index, int limit) { + // Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this). + // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII). + while (index < limit && bytes[index] >= 0) { + index++; + } + + return (index >= limit) ? COMPLETE : partialIsValidUtf8NonAscii(bytes, index, limit); + } + + private static int partialIsValidUtf8NonAscii(byte[] bytes, int index, int limit) { + for (;;) { + int byte1, byte2; + + // Optimize for interior runs of ASCII bytes. + do { + if (index >= limit) { + return COMPLETE; + } + } while ((byte1 = bytes[index++]) >= 0); + + if (byte1 < (byte) 0xE0) { + // two-byte form + + if (index >= limit) { + // Incomplete sequence + return byte1; + } + + // Simultaneously checks for illegal trailing-byte in + // leading position and overlong 2-byte form. + if (byte1 < (byte) 0xC2 + || bytes[index++] > (byte) 0xBF) { + return MALFORMED; + } + } else if (byte1 < (byte) 0xF0) { + // three-byte form + + if (index >= limit - 1) { // incomplete sequence + return incompleteStateFor(bytes, index, limit); + } + if ((byte2 = bytes[index++]) > (byte) 0xBF + // overlong? 5 most significant bits must not all be zero + || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) + // check for illegal surrogate codepoints + || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) + // byte3 trailing-byte test + || bytes[index++] > (byte) 0xBF) { + return MALFORMED; + } + } else { + // four-byte form + + if (index >= limit - 2) { // incomplete sequence + return incompleteStateFor(bytes, index, limit); + } + if ((byte2 = bytes[index++]) > (byte) 0xBF + // Check that 1 <= plane <= 16. Tricky optimized form of: + // if (byte1 > (byte) 0xF4 || + // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || + // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) + || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 + // byte3 trailing-byte test + || bytes[index++] > (byte) 0xBF + // byte4 trailing-byte test + || bytes[index++] > (byte) 0xBF) { + return MALFORMED; + } + } + } + } + } + + /** + * {@link Processor} that uses {@code sun.misc.Unsafe} where possible to improve performance. + */ + static final class UnsafeProcessor extends Processor { + private static final sun.misc.Unsafe UNSAFE = getUnsafe(); + private static final long BUFFER_ADDRESS_OFFSET = + fieldOffset(field(Buffer.class, "address")); + private static final int ARRAY_BASE_OFFSET = byteArrayBaseOffset(); + + /** + * We only use Unsafe operations if we have access to direct {@link ByteBuffer}'s address + * and the array base offset is a multiple of 8 (needed by Unsafe.getLong()). + */ + private static final boolean AVAILABLE = + BUFFER_ADDRESS_OFFSET != -1 && ARRAY_BASE_OFFSET % 8 == 0; + + /** + * Indicates whether or not all required unsafe operations are supported on this platform. + */ + static boolean isAvailable() { + return AVAILABLE; + } + + @Override + int partialIsValidUtf8(int state, byte[] bytes, final int index, final int limit) { + if ((index | limit | bytes.length - limit) < 0) { + throw new ArrayIndexOutOfBoundsException( + String.format("Array length=%d, index=%d, limit=%d", bytes.length, index, limit)); + } + long offset = ARRAY_BASE_OFFSET + index; + final long offsetLimit = ARRAY_BASE_OFFSET + limit; + if (state != COMPLETE) { + // The previous decoding operation was incomplete (or malformed). + // We look for a well-formed sequence consisting of bytes from + // the previous decoding operation (stored in state) together + // with bytes from the array slice. + // + // We expect such "straddler characters" to be rare. + + if (offset >= offsetLimit) { // No bytes? No progress. + return state; + } + int byte1 = (byte) state; + // byte1 is never ASCII. + if (byte1 < (byte) 0xE0) { + // two-byte form + + // Simultaneously checks for illegal trailing-byte in + // leading position and overlong 2-byte form. + if (byte1 < (byte) 0xC2 + // byte2 trailing-byte test + || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) { + return MALFORMED; + } + } else if (byte1 < (byte) 0xF0) { + // three-byte form + + // Get byte2 from saved state or array + int byte2 = (byte) ~(state >> 8); + if (byte2 == 0) { + byte2 = UNSAFE.getByte(bytes, offset++); + if (offset >= offsetLimit) { + return incompleteStateFor(byte1, byte2); + } + } + if (byte2 > (byte) 0xBF + // overlong? 5 most significant bits must not all be zero + || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) + // illegal surrogate codepoint? + || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) + // byte3 trailing-byte test + || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) { + return MALFORMED; + } + } else { + // four-byte form + + // Get byte2 and byte3 from saved state or array + int byte2 = (byte) ~(state >> 8); + int byte3 = 0; + if (byte2 == 0) { + byte2 = UNSAFE.getByte(bytes, offset++); + if (offset >= offsetLimit) { + return incompleteStateFor(byte1, byte2); + } + } else { + byte3 = (byte) (state >> 16); + } + if (byte3 == 0) { + byte3 = UNSAFE.getByte(bytes, offset++); + if (offset >= offsetLimit) { + return incompleteStateFor(byte1, byte2, byte3); + } + } + + // If we were called with state == MALFORMED, then byte1 is 0xFF, + // which never occurs in well-formed UTF-8, and so we will return + // MALFORMED again below. + + if (byte2 > (byte) 0xBF + // Check that 1 <= plane <= 16. Tricky optimized form of: + // if (byte1 > (byte) 0xF4 || + // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || + // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) + || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 + // byte3 trailing-byte test + || byte3 > (byte) 0xBF + // byte4 trailing-byte test + || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) { + return MALFORMED; + } + } + } + + return partialIsValidUtf8(bytes, offset, (int) (offsetLimit - offset)); + } + + @Override + int partialIsValidUtf8Direct( + final int state, ByteBuffer buffer, final int index, final int limit) { + if ((index | limit | buffer.limit() - limit) < 0) { + throw new ArrayIndexOutOfBoundsException( + String.format("buffer limit=%d, index=%d, limit=%d", buffer.limit(), index, limit)); + } + long address = addressOffset(buffer) + index; + final long addressLimit = address + (limit - index); + if (state != COMPLETE) { + // The previous decoding operation was incomplete (or malformed). + // We look for a well-formed sequence consisting of bytes from + // the previous decoding operation (stored in state) together + // with bytes from the array slice. + // + // We expect such "straddler characters" to be rare. + + if (address >= addressLimit) { // No bytes? No progress. + return state; + } + + final int byte1 = (byte) state; + // byte1 is never ASCII. + if (byte1 < (byte) 0xE0) { + // two-byte form + + // Simultaneously checks for illegal trailing-byte in + // leading position and overlong 2-byte form. + if (byte1 < (byte) 0xC2 + // byte2 trailing-byte test + || UNSAFE.getByte(address++) > (byte) 0xBF) { + return MALFORMED; + } + } else if (byte1 < (byte) 0xF0) { + // three-byte form + + // Get byte2 from saved state or array + int byte2 = (byte) ~(state >> 8); + if (byte2 == 0) { + byte2 = UNSAFE.getByte(address++); + if (address >= addressLimit) { + return incompleteStateFor(byte1, byte2); + } + } + if (byte2 > (byte) 0xBF + // overlong? 5 most significant bits must not all be zero + || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) + // illegal surrogate codepoint? + || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) + // byte3 trailing-byte test + || UNSAFE.getByte(address++) > (byte) 0xBF) { + return MALFORMED; + } + } else { + // four-byte form + + // Get byte2 and byte3 from saved state or array + int byte2 = (byte) ~(state >> 8); + int byte3 = 0; + if (byte2 == 0) { + byte2 = UNSAFE.getByte(address++); + if (address >= addressLimit) { + return incompleteStateFor(byte1, byte2); + } + } else { + byte3 = (byte) (state >> 16); + } + if (byte3 == 0) { + byte3 = UNSAFE.getByte(address++); + if (address >= addressLimit) { + return incompleteStateFor(byte1, byte2, byte3); + } + } + + // If we were called with state == MALFORMED, then byte1 is 0xFF, + // which never occurs in well-formed UTF-8, and so we will return + // MALFORMED again below. + + if (byte2 > (byte) 0xBF + // Check that 1 <= plane <= 16. Tricky optimized form of: + // if (byte1 > (byte) 0xF4 || + // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || + // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) + || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 + // byte3 trailing-byte test + || byte3 > (byte) 0xBF + // byte4 trailing-byte test + || UNSAFE.getByte(address++) > (byte) 0xBF) { + return MALFORMED; + } + } + } + + return partialIsValidUtf8(address, (int) (addressLimit - address)); + } + + @Override + int encodeUtf8(final CharSequence in, final byte[] out, final int offset, final int length) { + long outIx = ARRAY_BASE_OFFSET + offset; + final long outLimit = outIx + length; + final int inLimit = in.length(); + if (inLimit > length || out.length - length < offset) { + // Not even enough room for an ASCII-encoded string. + throw new ArrayIndexOutOfBoundsException( + "Failed writing " + in.charAt(inLimit - 1) + " at index " + (offset + length)); + } + + // Designed to take advantage of + // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination + int inIx = 0; + for (char c; inIx < inLimit && (c = in.charAt(inIx)) < 0x80; ++inIx) { + UNSAFE.putByte(out, outIx++, (byte) c); + } + if (inIx == inLimit) { + // We're done, it was ASCII encoded. + return (int) (outIx - ARRAY_BASE_OFFSET); + } + + for (char c; inIx < inLimit; ++inIx) { + c = in.charAt(inIx); + if (c < 0x80 && outIx < outLimit) { + UNSAFE.putByte(out, outIx++, (byte) c); + } else if (c < 0x800 && outIx <= outLimit - 2L) { // 11 bits, two UTF-8 bytes + UNSAFE.putByte(out, outIx++, (byte) ((0xF << 6) | (c >>> 6))); + UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & c))); + } else if ((c < MIN_SURROGATE || MAX_SURROGATE < c) && outIx <= outLimit - 3L) { + // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes + UNSAFE.putByte(out, outIx++, (byte) ((0xF << 5) | (c >>> 12))); + UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & (c >>> 6)))); + UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & c))); + } else if (outIx <= outLimit - 4L) { + // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8 + // bytes + final char low; + if (inIx + 1 == inLimit || !isSurrogatePair(c, (low = in.charAt(++inIx)))) { + throw new UnpairedSurrogateException((inIx - 1), inLimit); + } + int codePoint = toCodePoint(c, low); + UNSAFE.putByte(out, outIx++, (byte) ((0xF << 4) | (codePoint >>> 18))); + UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12)))); + UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6)))); + UNSAFE.putByte(out, outIx++, (byte) (0x80 | (0x3F & codePoint))); + } else { + if ((MIN_SURROGATE <= c && c <= MAX_SURROGATE) + && (inIx + 1 == inLimit || !isSurrogatePair(c, in.charAt(inIx + 1)))) { + // We are surrogates and we're not a surrogate pair. + throw new UnpairedSurrogateException(inIx, inLimit); + } + // Not enough space in the output buffer. + throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + outIx); + } + } + + // All bytes have been encoded. + return (int) (outIx - ARRAY_BASE_OFFSET); + } + + @Override + void encodeUtf8Direct(CharSequence in, ByteBuffer out) { + final long address = addressOffset(out); + long outIx = address + out.position(); + final long outLimit = address + out.limit(); + final int inLimit = in.length(); + if (inLimit > outLimit - outIx) { + // Not even enough room for an ASCII-encoded string. + throw new ArrayIndexOutOfBoundsException( + "Failed writing " + in.charAt(inLimit - 1) + " at index " + out.limit()); + } + + // Designed to take advantage of + // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination + int inIx = 0; + for (char c; inIx < inLimit && (c = in.charAt(inIx)) < 0x80; ++inIx) { + UNSAFE.putByte(outIx++, (byte) c); + } + if (inIx == inLimit) { + // We're done, it was ASCII encoded. + out.position((int) (outIx - address)); + return; + } + + for (char c; inIx < inLimit; ++inIx) { + c = in.charAt(inIx); + if (c < 0x80 && outIx < outLimit) { + UNSAFE.putByte(outIx++, (byte) c); + } else if (c < 0x800 && outIx <= outLimit - 2L) { // 11 bits, two UTF-8 bytes + UNSAFE.putByte(outIx++, (byte) ((0xF << 6) | (c >>> 6))); + UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & c))); + } else if ((c < MIN_SURROGATE || MAX_SURROGATE < c) && outIx <= outLimit - 3L) { + // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes + UNSAFE.putByte(outIx++, (byte) ((0xF << 5) | (c >>> 12))); + UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & (c >>> 6)))); + UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & c))); + } else if (outIx <= outLimit - 4L) { + // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8 + // bytes + final char low; + if (inIx + 1 == inLimit || !isSurrogatePair(c, (low = in.charAt(++inIx)))) { + throw new UnpairedSurrogateException((inIx - 1), inLimit); + } + int codePoint = toCodePoint(c, low); + UNSAFE.putByte(outIx++, (byte) ((0xF << 4) | (codePoint >>> 18))); + UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12)))); + UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6)))); + UNSAFE.putByte(outIx++, (byte) (0x80 | (0x3F & codePoint))); + } else { + if ((MIN_SURROGATE <= c && c <= MAX_SURROGATE) + && (inIx + 1 == inLimit || !isSurrogatePair(c, in.charAt(inIx + 1)))) { + // We are surrogates and we're not a surrogate pair. + throw new UnpairedSurrogateException(inIx, inLimit); + } + // Not enough space in the output buffer. + throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + outIx); + } + } + + // All bytes have been encoded. + out.position((int) (outIx - address)); + } + + /** + * Counts (approximately) the number of consecutive ASCII characters starting from the given + * position, using the most efficient method available to the platform. + * + * @param bytes the array containing the character sequence + * @param offset the offset position of the index (same as index + arrayBaseOffset) + * @param maxChars the maximum number of characters to count + * @return the number of ASCII characters found. The stopping position will be at or + * before the first non-ASCII byte. + */ + private static int unsafeEstimateConsecutiveAscii( + byte[] bytes, long offset, final int maxChars) { + int remaining = maxChars; + if (remaining < UNSAFE_COUNT_ASCII_THRESHOLD) { + // Don't bother with small strings. + return 0; + } + + // Read bytes until 8-byte aligned so that we can read longs in the loop below. + // Byte arrays are already either 8 or 16-byte aligned, so we just need to make sure that + // the index (relative to the start of the array) is also 8-byte aligned. We do this by + // ANDing the index with 7 to determine the number of bytes that need to be read before + // we're 8-byte aligned. + final int unaligned = (int) offset & 7; + for (int j = unaligned; j > 0; j--) { + if (UNSAFE.getByte(bytes, offset++) < 0) { + return unaligned - j; + } + } + + // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII). + // To speed things up further, we're reading longs instead of bytes so we use a mask to + // determine if any byte in the current long is non-ASCII. + remaining -= unaligned; + for (; remaining >= 8 && (UNSAFE.getLong(bytes, offset) & ASCII_MASK_LONG) == 0; + offset += 8, remaining -= 8) {} + return maxChars - remaining; + } + + /** + * Same as {@link Utf8#estimateConsecutiveAscii(ByteBuffer, int, int)} except that it uses the + * most efficient method available to the platform. + */ + private static int unsafeEstimateConsecutiveAscii(long address, final int maxChars) { + int remaining = maxChars; + if (remaining < UNSAFE_COUNT_ASCII_THRESHOLD) { + // Don't bother with small strings. + return 0; + } + + // Read bytes until 8-byte aligned so that we can read longs in the loop below. + // We do this by ANDing the address with 7 to determine the number of bytes that need to + // be read before we're 8-byte aligned. + final int unaligned = (int) address & 7; + for (int j = unaligned; j > 0; j--) { + if (UNSAFE.getByte(address++) < 0) { + return unaligned - j; + } + } + + // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII). + // To speed things up further, we're reading longs instead of bytes so we use a mask to + // determine if any byte in the current long is non-ASCII. + remaining -= unaligned; + for (; remaining >= 8 && (UNSAFE.getLong(address) & ASCII_MASK_LONG) == 0; + address += 8, remaining -= 8) {} + return maxChars - remaining; + } + + private static int partialIsValidUtf8(final byte[] bytes, long offset, int remaining) { + // Skip past ASCII characters as quickly as possible. + final int skipped = unsafeEstimateConsecutiveAscii(bytes, offset, remaining); + remaining -= skipped; + offset += skipped; + + for (;;) { + // Optimize for interior runs of ASCII bytes. + // TODO(nathanmittler): Consider checking 8 bytes at a time after some threshold? + // Maybe after seeing a few in a row that are ASCII, go back to fast mode? + int byte1 = 0; + for (; remaining > 0 && (byte1 = UNSAFE.getByte(bytes, offset++)) >= 0; --remaining) { + } + if (remaining == 0) { + return COMPLETE; + } + remaining--; + + // If we're here byte1 is not ASCII. Only need to handle 2-4 byte forms. + if (byte1 < (byte) 0xE0) { + // Two-byte form (110xxxxx 10xxxxxx) + if (remaining == 0) { + // Incomplete sequence + return byte1; + } + remaining--; + + // Simultaneously checks for illegal trailing-byte in + // leading position and overlong 2-byte form. + if (byte1 < (byte) 0xC2 + || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) { + return MALFORMED; + } + } else if (byte1 < (byte) 0xF0) { + // Three-byte form (1110xxxx 10xxxxxx 10xxxxxx) + if (remaining < 2) { + // Incomplete sequence + return unsafeIncompleteStateFor(bytes, byte1, offset, remaining); + } + remaining -= 2; + + final int byte2; + if ((byte2 = UNSAFE.getByte(bytes, offset++)) > (byte) 0xBF + // overlong? 5 most significant bits must not all be zero + || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) + // check for illegal surrogate codepoints + || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) + // byte3 trailing-byte test + || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) { + return MALFORMED; + } + } else { + // Four-byte form (1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx) + if (remaining < 3) { + // Incomplete sequence + return unsafeIncompleteStateFor(bytes, byte1, offset, remaining); + } + remaining -= 3; + + final int byte2; + if ((byte2 = UNSAFE.getByte(bytes, offset++)) > (byte) 0xBF + // Check that 1 <= plane <= 16. Tricky optimized form of: + // if (byte1 > (byte) 0xF4 || + // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || + // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) + || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 + // byte3 trailing-byte test + || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF + // byte4 trailing-byte test + || UNSAFE.getByte(bytes, offset++) > (byte) 0xBF) { + return MALFORMED; + } + } + } + } + + private static int partialIsValidUtf8(long address, int remaining) { + // Skip past ASCII characters as quickly as possible. + final int skipped = unsafeEstimateConsecutiveAscii(address, remaining); + address += skipped; + remaining -= skipped; + + for (;;) { + // Optimize for interior runs of ASCII bytes. + // TODO(nathanmittler): Consider checking 8 bytes at a time after some threshold? + // Maybe after seeing a few in a row that are ASCII, go back to fast mode? + int byte1 = 0; + for (; remaining > 0 && (byte1 = UNSAFE.getByte(address++)) >= 0; --remaining) { + } + if (remaining == 0) { + return COMPLETE; + } + remaining--; + + if (byte1 < (byte) 0xE0) { + // Two-byte form + + if (remaining == 0) { + // Incomplete sequence + return byte1; + } + remaining--; + + // Simultaneously checks for illegal trailing-byte in + // leading position and overlong 2-byte form. + if (byte1 < (byte) 0xC2 || UNSAFE.getByte(address++) > (byte) 0xBF) { + return MALFORMED; + } + } else if (byte1 < (byte) 0xF0) { + // Three-byte form + + if (remaining < 2) { + // Incomplete sequence + return unsafeIncompleteStateFor(address, byte1, remaining); + } + remaining -= 2; + + final byte byte2 = UNSAFE.getByte(address++); + if (byte2 > (byte) 0xBF + // overlong? 5 most significant bits must not all be zero + || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) + // check for illegal surrogate codepoints + || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) + // byte3 trailing-byte test + || UNSAFE.getByte(address++) > (byte) 0xBF) { + return MALFORMED; + } + } else { + // Four-byte form + + if (remaining < 3) { + // Incomplete sequence + return unsafeIncompleteStateFor(address, byte1, remaining); + } + remaining -= 3; + + final byte byte2 = UNSAFE.getByte(address++); + if (byte2 > (byte) 0xBF + // Check that 1 <= plane <= 16. Tricky optimized form of: + // if (byte1 > (byte) 0xF4 || + // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || + // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) + || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 + // byte3 trailing-byte test + || UNSAFE.getByte(address++) > (byte) 0xBF + // byte4 trailing-byte test + || UNSAFE.getByte(address++) > (byte) 0xBF) { + return MALFORMED; + } + } + } + } + + private static int unsafeIncompleteStateFor(byte[] bytes, int byte1, long offset, + int remaining) { + switch (remaining) { + case 0: { + return incompleteStateFor(byte1); + } + case 1: { + return incompleteStateFor(byte1, UNSAFE.getByte(bytes, offset)); + } + case 2: { + return incompleteStateFor(byte1, UNSAFE.getByte(bytes, offset), + UNSAFE.getByte(bytes, offset + 1)); + } + default: { + throw new AssertionError(); + } + } + } + + private static int unsafeIncompleteStateFor(long address, final int byte1, int remaining) { + switch (remaining) { + case 0: { + return incompleteStateFor(byte1); + } + case 1: { + return incompleteStateFor(byte1, UNSAFE.getByte(address)); + } + case 2: { + return incompleteStateFor(byte1, UNSAFE.getByte(address), UNSAFE.getByte(address + 1)); + } + default: { + throw new AssertionError(); + } + } + } + + /** + * Gets the field with the given name within the class, or {@code null} if not found. If + * found, the field is made accessible. + */ + private static Field field(Class clazz, String fieldName) { + Field field; + try { + field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + } catch (Throwable t) { + // Failed to access the fields. + field = null; + } + logger.log(Level.FINEST, "{0}.{1}: {2}", + new Object[] {clazz.getName(), fieldName, (field != null ? "available" : "unavailable")}); + return field; + } + + /** + * Returns the offset of the provided field, or {@code -1} if {@code sun.misc.Unsafe} is not + * available. + */ + private static long fieldOffset(Field field) { + return field == null || UNSAFE == null ? -1 : UNSAFE.objectFieldOffset(field); + } + + /** + * Get the base offset for byte arrays, or {@code -1} if {@code sun.misc.Unsafe} is not + * available. + */ + private static int byteArrayBaseOffset() { + return UNSAFE == null ? -1 : UNSAFE.arrayBaseOffset(byte[].class); + } + + /** + * Gets the offset of the {@code address} field of the given direct {@link ByteBuffer}. + */ + private static long addressOffset(ByteBuffer buffer) { + return UNSAFE.getLong(buffer, BUFFER_ADDRESS_OFFSET); + } + + /** + * Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this + * platform. + */ + private static sun.misc.Unsafe getUnsafe() { + sun.misc.Unsafe unsafe = null; + try { + unsafe = AccessController.doPrivileged(new PrivilegedExceptionAction() { + @Override + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + + // Check that this platform supports all of the required unsafe methods. + checkRequiredMethods(k); + + for (Field f : k.getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if (k.isInstance(x)) { + return k.cast(x); + } + } + // The sun.misc.Unsafe field does not exist. + return null; + } + }); + } catch (Throwable e) { + // Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError + // for Unsafe. + } + + logger.log(Level.FINEST, "sun.misc.Unsafe: {}", + unsafe != null ? "available" : "unavailable"); + return unsafe; + } + + /** + * Verifies that all required methods of {@code sun.misc.Unsafe} are available on this platform. + */ + private static void checkRequiredMethods(Class clazz) + throws NoSuchMethodException, SecurityException { + // Needed for Unsafe byte[] access + clazz.getMethod("arrayBaseOffset", Class.class); + clazz.getMethod("getByte", Object.class, long.class); + clazz.getMethod("putByte", Object.class, long.class, byte.class); + clazz.getMethod("getLong", Object.class, long.class); + + // Needed for Unsafe Direct ByteBuffer access + clazz.getMethod("objectFieldOffset", Field.class); + clazz.getMethod("getByte", long.class); + clazz.getMethod("getLong", Object.class, long.class); + clazz.getMethod("putByte", long.class, byte.class); + clazz.getMethod("getLong", long.class); + } + } + + private Utf8() {} } diff --git a/java/core/src/main/java/com/google/protobuf/WireFormat.java b/java/core/src/main/java/com/google/protobuf/WireFormat.java index 8dbe1ae33..6a58b0811 100644 --- a/java/core/src/main/java/com/google/protobuf/WireFormat.java +++ b/java/core/src/main/java/com/google/protobuf/WireFormat.java @@ -116,16 +116,24 @@ public final class WireFormat { FIXED32 (JavaType.INT , WIRETYPE_FIXED32 ), BOOL (JavaType.BOOLEAN , WIRETYPE_VARINT ), STRING (JavaType.STRING , WIRETYPE_LENGTH_DELIMITED) { - public boolean isPackable() { return false; } + @Override + public boolean isPackable() { + return false; } }, GROUP (JavaType.MESSAGE , WIRETYPE_START_GROUP ) { - public boolean isPackable() { return false; } + @Override + public boolean isPackable() { + return false; } }, MESSAGE (JavaType.MESSAGE , WIRETYPE_LENGTH_DELIMITED) { - public boolean isPackable() { return false; } + @Override + public boolean isPackable() { + return false; } }, BYTES (JavaType.BYTE_STRING, WIRETYPE_LENGTH_DELIMITED) { - public boolean isPackable() { return false; } + @Override + public boolean isPackable() { + return false; } }, UINT32 (JavaType.INT , WIRETYPE_VARINT ), ENUM (JavaType.ENUM , WIRETYPE_VARINT ), @@ -170,18 +178,21 @@ public final class WireFormat { enum Utf8Validation { /** Eagerly parses to String; silently accepts invalid UTF8 bytes. */ LOOSE { + @Override Object readString(CodedInputStream input) throws IOException { return input.readString(); } }, /** Eagerly parses to String; throws an IOException on invalid bytes. */ STRICT { + @Override Object readString(CodedInputStream input) throws IOException { return input.readStringRequireUtf8(); } }, /** Keep data as ByteString; validation/conversion to String is lazy. */ LAZY { + @Override Object readString(CodedInputStream input) throws IOException { return input.readBytes(); } diff --git a/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java b/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java index d964ef596..7dc9fc150 100644 --- a/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java +++ b/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java @@ -40,7 +40,6 @@ import protobuf_unittest.UnittestProto.TestPackedTypes; import protobuf_unittest.UnittestProto.TestRequired; import protobuf_unittest.UnittestProto.TestRequiredForeign; import protobuf_unittest.UnittestProto.TestUnpackedTypes; - import junit.framework.TestCase; import java.util.Map; @@ -65,35 +64,44 @@ public class AbstractMessageTest extends TestCase { this.wrappedMessage = wrappedMessage; } + @Override public Descriptors.Descriptor getDescriptorForType() { return wrappedMessage.getDescriptorForType(); } + @Override public AbstractMessageWrapper getDefaultInstanceForType() { return new AbstractMessageWrapper( wrappedMessage.getDefaultInstanceForType()); } + @Override public Map getAllFields() { return wrappedMessage.getAllFields(); } + @Override public boolean hasField(Descriptors.FieldDescriptor field) { return wrappedMessage.hasField(field); } + @Override public Object getField(Descriptors.FieldDescriptor field) { return wrappedMessage.getField(field); } + @Override public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { return wrappedMessage.getRepeatedFieldCount(field); } - public Object getRepeatedField( - Descriptors.FieldDescriptor field, int index) { + @Override + public Object getRepeatedField(Descriptors.FieldDescriptor field, int index) { return wrappedMessage.getRepeatedField(field, index); } + @Override public UnknownFieldSet getUnknownFields() { return wrappedMessage.getUnknownFields(); } + @Override public Builder newBuilderForType() { return new Builder(wrappedMessage.newBuilderForType()); } + @Override public Builder toBuilder() { return new Builder(wrappedMessage.toBuilder()); } @@ -105,65 +113,80 @@ public class AbstractMessageTest extends TestCase { this.wrappedBuilder = wrappedBuilder; } + @Override public AbstractMessageWrapper build() { return new AbstractMessageWrapper(wrappedBuilder.build()); } + @Override public AbstractMessageWrapper buildPartial() { return new AbstractMessageWrapper(wrappedBuilder.buildPartial()); } + @Override public Builder clone() { return new Builder(wrappedBuilder.clone()); } + @Override public boolean isInitialized() { return clone().buildPartial().isInitialized(); } + @Override public Descriptors.Descriptor getDescriptorForType() { return wrappedBuilder.getDescriptorForType(); } + @Override public AbstractMessageWrapper getDefaultInstanceForType() { return new AbstractMessageWrapper( wrappedBuilder.getDefaultInstanceForType()); } + @Override public Map getAllFields() { return wrappedBuilder.getAllFields(); } + @Override public Builder newBuilderForField(Descriptors.FieldDescriptor field) { return new Builder(wrappedBuilder.newBuilderForField(field)); } + @Override public boolean hasField(Descriptors.FieldDescriptor field) { return wrappedBuilder.hasField(field); } + @Override public Object getField(Descriptors.FieldDescriptor field) { return wrappedBuilder.getField(field); } + @Override public Builder setField(Descriptors.FieldDescriptor field, Object value) { wrappedBuilder.setField(field, value); return this; } + @Override public Builder clearField(Descriptors.FieldDescriptor field) { wrappedBuilder.clearField(field); return this; } + @Override public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { return wrappedBuilder.getRepeatedFieldCount(field); } - public Object getRepeatedField( - Descriptors.FieldDescriptor field, int index) { + @Override + public Object getRepeatedField(Descriptors.FieldDescriptor field, int index) { return wrappedBuilder.getRepeatedField(field, index); } - public Builder setRepeatedField(Descriptors.FieldDescriptor field, - int index, Object value) { + @Override + public Builder setRepeatedField(Descriptors.FieldDescriptor field, int index, Object value) { wrappedBuilder.setRepeatedField(field, index, value); return this; } - public Builder addRepeatedField( - Descriptors.FieldDescriptor field, Object value) { + @Override + public Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value) { wrappedBuilder.addRepeatedField(field, value); return this; } + @Override public UnknownFieldSet getUnknownFields() { return wrappedBuilder.getUnknownFields(); } + @Override public Builder setUnknownFields(UnknownFieldSet unknownFields) { wrappedBuilder.setUnknownFields(unknownFields); return this; @@ -173,6 +196,7 @@ public class AbstractMessageTest extends TestCase { return wrappedBuilder.getFieldBuilder(field); } } + @Override public Parser getParserForType() { return wrappedMessage.getParserForType(); } diff --git a/java/core/src/test/java/com/google/protobuf/AnyTest.java b/java/core/src/test/java/com/google/protobuf/AnyTest.java index e169f69d2..cf91ed91c 100644 --- a/java/core/src/test/java/com/google/protobuf/AnyTest.java +++ b/java/core/src/test/java/com/google/protobuf/AnyTest.java @@ -75,6 +75,51 @@ public class AnyTest extends TestCase { } } + public void testCustomTypeUrls() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestAllTypes message = builder.build(); + + TestAny container = TestAny.newBuilder() + .setValue(Any.pack(message, "xxx.com")).build(); + + assertEquals( + "xxx.com/" + TestAllTypes.getDescriptor().getFullName(), + container.getValue().getTypeUrl()); + + assertTrue(container.getValue().is(TestAllTypes.class)); + assertFalse(container.getValue().is(TestAny.class)); + + TestAllTypes result = container.getValue().unpack(TestAllTypes.class); + TestUtil.assertAllFieldsSet(result); + + container = TestAny.newBuilder() + .setValue(Any.pack(message, "yyy.com/")).build(); + + assertEquals( + "yyy.com/" + TestAllTypes.getDescriptor().getFullName(), + container.getValue().getTypeUrl()); + + assertTrue(container.getValue().is(TestAllTypes.class)); + assertFalse(container.getValue().is(TestAny.class)); + + result = container.getValue().unpack(TestAllTypes.class); + TestUtil.assertAllFieldsSet(result); + + container = TestAny.newBuilder() + .setValue(Any.pack(message, "")).build(); + + assertEquals( + "/" + TestAllTypes.getDescriptor().getFullName(), + container.getValue().getTypeUrl()); + + assertTrue(container.getValue().is(TestAllTypes.class)); + assertFalse(container.getValue().is(TestAny.class)); + + result = container.getValue().unpack(TestAllTypes.class); + TestUtil.assertAllFieldsSet(result); + } + public void testCachedUnpackResult() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TestUtil.setAllFields(builder); diff --git a/java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java b/java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java index b8ad1fe4c..24b96c602 100644 --- a/java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java +++ b/java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java @@ -73,20 +73,6 @@ public class BooleanArrayListTest extends TestCase { assertImmutable(list); } - public void testCopyConstructor() { - BooleanArrayList copy = new BooleanArrayList(TERTIARY_LIST); - assertEquals(TERTIARY_LIST, copy); - - copy = new BooleanArrayList(BooleanArrayList.emptyList()); - assertEquals(BooleanArrayList.emptyList(), copy); - - copy = new BooleanArrayList(asList(false, false, true)); - assertEquals(asList(false, false, true), copy); - - copy = new BooleanArrayList(Collections.emptyList()); - assertEquals(BooleanArrayList.emptyList(), copy); - } - public void testModificationWithIteration() { list.addAll(asList(true, false, false, true)); Iterator iterator = list.iterator(); diff --git a/java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java b/java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java index 7a8a0a5e7..db10ee748 100644 --- a/java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java +++ b/java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java @@ -37,7 +37,6 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.UnsupportedEncodingException; - /** * This class tests {@link BoundedByteString}, which extends {@link LiteralByteString}, * by inheriting the tests from {@link LiteralByteStringTest}. The only method which diff --git a/java/core/src/test/java/com/google/protobuf/ByteBufferWriterTest.java b/java/core/src/test/java/com/google/protobuf/ByteBufferWriterTest.java new file mode 100644 index 000000000..cbe742e5b --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/ByteBufferWriterTest.java @@ -0,0 +1,81 @@ +// 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. + +package com.google.protobuf; + +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Random; + +/** + * Tests for {@link ByteBufferWriter}. + */ +public class ByteBufferWriterTest extends TestCase { + + public void testHeapBuffer() throws IOException { + // Test a small and large buffer. + testWrite(ByteBuffer.allocate(100)); + testWrite(ByteBuffer.allocate(1024 * 100)); + } + + public void testDirectBuffer() throws IOException { + // Test a small and large buffer. + testWrite(ByteBuffer.allocateDirect(100)); + testWrite(ByteBuffer.allocateDirect(1024 * 100)); + } + + private void testWrite(ByteBuffer buffer) throws IOException { + fillRandom(buffer); + ByteArrayOutputStream os = new ByteArrayOutputStream(buffer.remaining()); + ByteBufferWriter.write(buffer, os); + assertEquals(0, buffer.position()); + assertTrue(Arrays.equals(toArray(buffer), os.toByteArray())); + } + + private void fillRandom(ByteBuffer buf) { + byte[] bytes = new byte[buf.remaining()]; + new Random().nextBytes(bytes); + buf.put(bytes); + buf.flip(); + return; + } + + private byte[] toArray(ByteBuffer buf) { + int originalPosition = buf.position(); + byte[] bytes = new byte[buf.remaining()]; + buf.get(bytes); + buf.position(originalPosition); + return bytes; + } +} diff --git a/java/core/src/test/java/com/google/protobuf/ByteStringTest.java b/java/core/src/test/java/com/google/protobuf/ByteStringTest.java index 5267c160e..ad9f266e4 100644 --- a/java/core/src/test/java/com/google/protobuf/ByteStringTest.java +++ b/java/core/src/test/java/com/google/protobuf/ByteStringTest.java @@ -39,6 +39,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; @@ -757,4 +758,17 @@ public class ByteStringTest extends TestCase { assertEquals((byte) 2, result[dataSize - dataSize / 2]); assertEquals((byte) 2, result[dataSize - 1]); } + + /** + * Tests ByteString uses Arrays based byte copier when running under Hotstop VM. + */ + public void testByteArrayCopier() throws Exception { + Field field = ByteString.class.getDeclaredField("byteArrayCopier"); + field.setAccessible(true); + Object byteArrayCopier = field.get(null); + assertNotNull(byteArrayCopier); + assertTrue( + byteArrayCopier.toString(), + byteArrayCopier.getClass().getSimpleName().endsWith("ArraysByteArrayCopier")); + } } diff --git a/java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java b/java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java index 3d6381c9b..cc65d19ab 100644 --- a/java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java +++ b/java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java @@ -34,6 +34,7 @@ import proto2_test_check_utf8.TestCheckUtf8.BytesWrapper; import proto2_test_check_utf8.TestCheckUtf8.StringWrapper; import proto2_test_check_utf8_size.TestCheckUtf8Size.BytesWrapperSize; import proto2_test_check_utf8_size.TestCheckUtf8Size.StringWrapperSize; + import junit.framework.TestCase; /** diff --git a/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java index 18d8142c9..ca940cedb 100644 --- a/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java +++ b/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java @@ -81,10 +81,12 @@ public class CodedInputStreamTest extends TestCase { this.blockSize = blockSize; } + @Override public int read(byte[] b) throws IOException { return super.read(b, 0, Math.min(b.length, blockSize)); } + @Override public int read(byte[] b, int off, int len) throws IOException { return super.read(b, off, Math.min(len, blockSize)); } @@ -547,6 +549,56 @@ public class CodedInputStreamTest extends TestCase { } } + public void testReadString() throws Exception { + String lorem = "Lorem ipsum dolor sit amet "; + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < 4096; i += lorem.length()) { + builder.append(lorem); + } + lorem = builder.toString().substring(0, 4096); + byte[] bytes = lorem.getBytes("UTF-8"); + ByteString.Output rawOutput = ByteString.newOutput(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length); + + int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); + output.writeRawVarint32(tag); + output.writeRawVarint32(bytes.length); + output.writeRawBytes(bytes); + output.flush(); + + CodedInputStream input = + CodedInputStream.newInstance( + new ByteArrayInputStream(rawOutput.toByteString().toByteArray())); + assertEquals(tag, input.readTag()); + String text = input.readString(); + assertEquals(lorem, text); + } + + public void testReadStringRequireUtf8() throws Exception { + String lorem = "Lorem ipsum dolor sit amet "; + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < 4096; i += lorem.length()) { + builder.append(lorem); + } + lorem = builder.toString().substring(0, 4096); + byte[] bytes = lorem.getBytes("UTF-8"); + ByteString.Output rawOutput = ByteString.newOutput(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length); + + int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); + output.writeRawVarint32(tag); + output.writeRawVarint32(bytes.length); + output.writeRawBytes(bytes); + output.flush(); + + CodedInputStream input = + CodedInputStream.newInstance( + new ByteArrayInputStream(rawOutput.toByteString().toByteArray())); + assertEquals(tag, input.readTag()); + String text = input.readStringRequireUtf8(); + assertEquals(lorem, text); + } + /** * Tests that if we readString invalid UTF-8 bytes, no exception * is thrown. Instead, the invalid bytes are replaced with the Unicode diff --git a/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java b/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java index 6018ea558..33aa43570 100644 --- a/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java +++ b/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java @@ -30,6 +30,7 @@ package com.google.protobuf; +import com.google.protobuf.CodedOutputStream.OutOfSpaceException; import protobuf_unittest.UnittestProto.SparseEnumMessage; import protobuf_unittest.UnittestProto.TestAllTypes; import protobuf_unittest.UnittestProto.TestPackedTypes; @@ -50,118 +51,180 @@ import java.util.List; * @author kenton@google.com Kenton Varda */ public class CodedOutputStreamTest extends TestCase { - /** - * 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. - */ - private byte[] bytes(int... bytesAsInts) { - byte[] bytes = new byte[bytesAsInts.length]; - for (int i = 0; i < bytesAsInts.length; i++) { - bytes[i] = (byte) bytesAsInts[i]; - } - return bytes; + private interface Coder { + CodedOutputStream stream(); + + byte[] toByteArray(); + + OutputType getOutputType(); } - /** Arrays.asList() does not work with arrays of primitives. :( */ - private List toList(byte[] bytes) { - List result = new ArrayList(); - for (byte b : bytes) { - result.add(b); + private static final class OutputStreamCoder implements Coder { + private final CodedOutputStream stream; + private final ByteArrayOutputStream output; + + OutputStreamCoder(int size) { + output = new ByteArrayOutputStream(); + stream = CodedOutputStream.newInstance(output, size); + } + + @Override + public CodedOutputStream stream() { + return stream; + } + + @Override + public byte[] toByteArray() { + return output.toByteArray(); + } + + @Override + public OutputType getOutputType() { + return OutputType.STREAM; } - return result; } - private void assertEqualBytes(byte[] a, byte[] b) { - assertEquals(toList(a), toList(b)); + private static final class ArrayCoder implements Coder { + private final CodedOutputStream stream; + private final byte[] bytes; + + ArrayCoder(int size) { + bytes = new byte[size]; + stream = CodedOutputStream.newInstance(bytes); + } + + @Override + public CodedOutputStream stream() { + return stream; + } + + @Override + public byte[] toByteArray() { + return Arrays.copyOf(bytes, stream.getTotalBytesWritten()); + } + + @Override + public OutputType getOutputType() { + return OutputType.ARRAY; + } } - /** - * Writes the given value using writeRawVarint32() and writeRawVarint64() and - * checks that the result matches the given bytes. - */ - private void assertWriteVarint(byte[] data, long value) throws Exception { - // Only test 32-bit write if the value fits into an int. - if (value == (int) value) { - ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); - CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); - output.writeRawVarint32((int) value); - output.flush(); - assertEqualBytes(data, rawOutput.toByteArray()); + private static final class NioHeapCoder implements Coder { + private final CodedOutputStream stream; + private final ByteBuffer buffer; + private final int initialPosition; - // Also try computing size. - assertEquals(data.length, - CodedOutputStream.computeRawVarint32Size((int) value)); + NioHeapCoder(int size) { + this(size, 0); } - { - ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); - CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); - output.writeRawVarint64(value); - output.flush(); - assertEqualBytes(data, rawOutput.toByteArray()); - - // Also try computing size. - assertEquals(data.length, - CodedOutputStream.computeRawVarint64Size(value)); + NioHeapCoder(int size, int initialPosition) { + this.initialPosition = initialPosition; + buffer = ByteBuffer.allocate(size); + buffer.position(initialPosition); + stream = CodedOutputStream.newInstance(buffer); } - // Try different block sizes. - for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { - // Only test 32-bit write if the value fits into an int. - if (value == (int) value) { - ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); - CodedOutputStream output = - CodedOutputStream.newInstance(rawOutput, blockSize); - output.writeRawVarint32((int) value); - output.flush(); - assertEqualBytes(data, rawOutput.toByteArray()); + @Override + public CodedOutputStream stream() { + return stream; + } + + @Override + public byte[] toByteArray() { + ByteBuffer dup = buffer.duplicate(); + dup.position(initialPosition); + dup.limit(buffer.position()); + + byte[] bytes = new byte[dup.remaining()]; + dup.get(bytes); + return bytes; + } + + @Override + public OutputType getOutputType() { + return OutputType.NIO_HEAP; + } + } + + private static final class NioDirectCoder implements Coder { + private final int initialPosition; + private final CodedOutputStream stream; + private final ByteBuffer buffer; + + NioDirectCoder(int size) { + this(size, 0); + } + + NioDirectCoder(int size, int initialPosition) { + this.initialPosition = initialPosition; + buffer = ByteBuffer.allocateDirect(size); + buffer.position(initialPosition); + stream = CodedOutputStream.newInstance(buffer); + } + + @Override + public CodedOutputStream stream() { + return stream; + } + + @Override + public byte[] toByteArray() { + ByteBuffer dup = buffer.duplicate(); + dup.position(initialPosition); + dup.limit(buffer.position()); + + byte[] bytes = new byte[dup.remaining()]; + dup.get(bytes); + return bytes; + } + + @Override + public OutputType getOutputType() { + return OutputType.NIO_DIRECT; + } + } + + private enum OutputType { + ARRAY() { + @Override + Coder newCoder(int size) { + return new ArrayCoder(size); } - - { - ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); - CodedOutputStream output = - CodedOutputStream.newInstance(rawOutput, blockSize); - output.writeRawVarint64(value); - output.flush(); - assertEqualBytes(data, rawOutput.toByteArray()); + }, + NIO_HEAP() { + @Override + Coder newCoder(int size) { + return new NioHeapCoder(size); } - } - } + }, + NIO_DIRECT() { + @Override + Coder newCoder(int size) { + return new NioDirectCoder(size); + } + }, + STREAM() { + @Override + Coder newCoder(int size) { + return new OutputStreamCoder(size); + } + }; - private void assertVarintRoundTrip(long value) throws Exception { - { - ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); - CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); - output.writeRawVarint64(value); - output.flush(); - byte[] bytes = rawOutput.toByteArray(); - assertEquals(bytes.length, CodedOutputStream.computeRawVarint64Size(value)); - CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(bytes)); - assertEquals(value, input.readRawVarint64()); - } - - if (value == (int) value) { - ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); - CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); - output.writeRawVarint32((int) value); - output.flush(); - byte[] bytes = rawOutput.toByteArray(); - assertEquals(bytes.length, CodedOutputStream.computeRawVarint32Size((int) value)); - CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(bytes)); - assertEquals(value, input.readRawVarint32()); - } + abstract Coder newCoder(int size); } /** Checks that invariants are maintained for varint round trip input and output. */ public void testVarintRoundTrips() throws Exception { - assertVarintRoundTrip(0L); - for (int bits = 0; bits < 64; bits++) { - long value = 1L << bits; - assertVarintRoundTrip(value); - assertVarintRoundTrip(value + 1); - assertVarintRoundTrip(value - 1); - assertVarintRoundTrip(-value); + for (OutputType outputType : OutputType.values()) { + assertVarintRoundTrip(outputType, 0L); + for (int bits = 0; bits < 64; bits++) { + long value = 1L << bits; + assertVarintRoundTrip(outputType, value); + assertVarintRoundTrip(outputType, value + 1); + assertVarintRoundTrip(outputType, value - 1); + assertVarintRoundTrip(outputType, -value); + } } } @@ -173,70 +236,25 @@ public class CodedOutputStreamTest extends TestCase { // 14882 assertWriteVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7)); // 2961488830 - assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b), - (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | - (0x0bL << 28)); + assertWriteVarint( + bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b), + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | (0x0bL << 28)); // 64-bit // 7256456126 - assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b), - (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | - (0x1bL << 28)); + assertWriteVarint( + bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b), + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | (0x1bL << 28)); // 41256202580718336 assertWriteVarint( - 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)); + 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 assertWriteVarint( - bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01), - (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) | - (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) | - (0x05L << 49) | (0x26L << 56) | (0x01L << 63)); - } - - /** - * Parses the given bytes using writeRawLittleEndian32() and checks - * that the result matches the given value. - */ - private void assertWriteLittleEndian32(byte[] data, int value) - throws Exception { - ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); - CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); - output.writeRawLittleEndian32(value); - output.flush(); - assertEqualBytes(data, rawOutput.toByteArray()); - - // Try different block sizes. - for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { - rawOutput = new ByteArrayOutputStream(); - output = CodedOutputStream.newInstance(rawOutput, blockSize); - output.writeRawLittleEndian32(value); - output.flush(); - assertEqualBytes(data, rawOutput.toByteArray()); - } - } - - /** - * Parses the given bytes using writeRawLittleEndian64() and checks - * that the result matches the given value. - */ - private void assertWriteLittleEndian64(byte[] data, long value) - throws Exception { - ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); - CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); - output.writeRawLittleEndian64(value); - output.flush(); - assertEqualBytes(data, rawOutput.toByteArray()); - - // Try different block sizes. - for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { - rawOutput = new ByteArrayOutputStream(); - output = CodedOutputStream.newInstance(rawOutput, blockSize); - output.writeRawLittleEndian64(value); - output.flush(); - assertEqualBytes(data, rawOutput.toByteArray()); - } + bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01), + (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) | (0x3bL << 28) | (0x56L << 35) + | (0x00L << 42) | (0x05L << 49) | (0x26L << 56) | (0x01L << 63)); } /** Tests writeRawLittleEndian32() and writeRawLittleEndian64(). */ @@ -245,141 +263,138 @@ public class CodedOutputStreamTest extends TestCase { assertWriteLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0); assertWriteLittleEndian64( - bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12), - 0x123456789abcdef0L); + bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12), 0x123456789abcdef0L); assertWriteLittleEndian64( - bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), - 0x9abcdef012345678L); + bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678L); } /** Test encodeZigZag32() and encodeZigZag64(). */ public void testEncodeZigZag() throws Exception { - assertEquals(0, CodedOutputStream.encodeZigZag32( 0)); + assertEquals(0, CodedOutputStream.encodeZigZag32(0)); assertEquals(1, CodedOutputStream.encodeZigZag32(-1)); - assertEquals(2, CodedOutputStream.encodeZigZag32( 1)); + assertEquals(2, CodedOutputStream.encodeZigZag32(1)); assertEquals(3, CodedOutputStream.encodeZigZag32(-2)); assertEquals(0x7FFFFFFE, CodedOutputStream.encodeZigZag32(0x3FFFFFFF)); assertEquals(0x7FFFFFFF, CodedOutputStream.encodeZigZag32(0xC0000000)); assertEquals(0xFFFFFFFE, CodedOutputStream.encodeZigZag32(0x7FFFFFFF)); assertEquals(0xFFFFFFFF, CodedOutputStream.encodeZigZag32(0x80000000)); - assertEquals(0, CodedOutputStream.encodeZigZag64( 0)); + assertEquals(0, CodedOutputStream.encodeZigZag64(0)); assertEquals(1, CodedOutputStream.encodeZigZag64(-1)); - assertEquals(2, CodedOutputStream.encodeZigZag64( 1)); + assertEquals(2, CodedOutputStream.encodeZigZag64(1)); assertEquals(3, CodedOutputStream.encodeZigZag64(-2)); - assertEquals(0x000000007FFFFFFEL, - CodedOutputStream.encodeZigZag64(0x000000003FFFFFFFL)); - assertEquals(0x000000007FFFFFFFL, - CodedOutputStream.encodeZigZag64(0xFFFFFFFFC0000000L)); - assertEquals(0x00000000FFFFFFFEL, - CodedOutputStream.encodeZigZag64(0x000000007FFFFFFFL)); - assertEquals(0x00000000FFFFFFFFL, - CodedOutputStream.encodeZigZag64(0xFFFFFFFF80000000L)); - assertEquals(0xFFFFFFFFFFFFFFFEL, - CodedOutputStream.encodeZigZag64(0x7FFFFFFFFFFFFFFFL)); - assertEquals(0xFFFFFFFFFFFFFFFFL, - CodedOutputStream.encodeZigZag64(0x8000000000000000L)); + assertEquals(0x000000007FFFFFFEL, CodedOutputStream.encodeZigZag64(0x000000003FFFFFFFL)); + assertEquals(0x000000007FFFFFFFL, CodedOutputStream.encodeZigZag64(0xFFFFFFFFC0000000L)); + assertEquals(0x00000000FFFFFFFEL, CodedOutputStream.encodeZigZag64(0x000000007FFFFFFFL)); + assertEquals(0x00000000FFFFFFFFL, CodedOutputStream.encodeZigZag64(0xFFFFFFFF80000000L)); + assertEquals(0xFFFFFFFFFFFFFFFEL, CodedOutputStream.encodeZigZag64(0x7FFFFFFFFFFFFFFFL)); + assertEquals(0xFFFFFFFFFFFFFFFFL, CodedOutputStream.encodeZigZag64(0x8000000000000000L)); // Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1) // were chosen semi-randomly via keyboard bashing. - assertEquals(0, - CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(0))); - assertEquals(1, - CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(1))); - assertEquals(-1, - CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-1))); - assertEquals(14927, - CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(14927))); - assertEquals(-3612, - CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-3612))); + assertEquals(0, CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(0))); + assertEquals(1, CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(1))); + assertEquals(-1, CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-1))); + assertEquals(14927, CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(14927))); + assertEquals(-3612, CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-3612))); - assertEquals(0, - CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(0))); - assertEquals(1, - CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(1))); - assertEquals(-1, - CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-1))); - assertEquals(14927, - CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(14927))); - assertEquals(-3612, - CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-3612))); + assertEquals(0, CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(0))); + assertEquals(1, CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(1))); + assertEquals(-1, CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-1))); + assertEquals(14927, CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(14927))); + assertEquals(-3612, CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-3612))); - assertEquals(856912304801416L, - CodedOutputStream.encodeZigZag64( - CodedInputStream.decodeZigZag64( - 856912304801416L))); - assertEquals(-75123905439571256L, - CodedOutputStream.encodeZigZag64( - CodedInputStream.decodeZigZag64( - -75123905439571256L))); + assertEquals( + 856912304801416L, + CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(856912304801416L))); + assertEquals( + -75123905439571256L, + CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-75123905439571256L))); } /** Tests writing a whole message with every field type. */ public void testWriteWholeMessage() throws Exception { + final byte[] expectedBytes = TestUtil.getGoldenMessage().toByteArray(); TestAllTypes message = TestUtil.getAllSet(); - byte[] rawBytes = message.toByteArray(); - assertEqualBytes(TestUtil.getGoldenMessage().toByteArray(), rawBytes); + for (OutputType outputType : OutputType.values()) { + Coder coder = outputType.newCoder(message.getSerializedSize()); + message.writeTo(coder.stream()); + coder.stream().flush(); + byte[] rawBytes = coder.toByteArray(); + assertEqualBytes(outputType, expectedBytes, rawBytes); + } // Try different block sizes. for (int blockSize = 1; blockSize < 256; blockSize *= 2) { - ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); - CodedOutputStream output = - CodedOutputStream.newInstance(rawOutput, blockSize); - message.writeTo(output); - output.flush(); - assertEqualBytes(rawBytes, rawOutput.toByteArray()); + Coder coder = OutputType.STREAM.newCoder(blockSize); + message.writeTo(coder.stream()); + coder.stream().flush(); + assertEqualBytes(OutputType.STREAM, expectedBytes, coder.toByteArray()); } } - /** Tests writing a whole message with every packed field type. Ensures the - * wire format of packed fields is compatible with C++. */ + /** + * Tests writing a whole message with every packed field type. Ensures the + * wire format of packed fields is compatible with C++. + */ public void testWriteWholePackedFieldsMessage() throws Exception { + byte[] expectedBytes = TestUtil.getGoldenPackedFieldsMessage().toByteArray(); TestPackedTypes message = TestUtil.getPackedSet(); - byte[] rawBytes = message.toByteArray(); - assertEqualBytes(TestUtil.getGoldenPackedFieldsMessage().toByteArray(), - rawBytes); + for (OutputType outputType : OutputType.values()) { + Coder coder = outputType.newCoder(message.getSerializedSize()); + message.writeTo(coder.stream()); + coder.stream().flush(); + byte[] rawBytes = coder.toByteArray(); + assertEqualBytes(outputType, expectedBytes, rawBytes); + } } - /** Test writing a message containing a negative enum value. This used to + /** + * Test writing a message containing a negative enum value. This used to * fail because the size was not properly computed as a sign-extended varint. */ public void testWriteMessageWithNegativeEnumValue() throws Exception { - SparseEnumMessage message = SparseEnumMessage.newBuilder() - .setSparseEnum(TestSparseEnum.SPARSE_E) .build(); + SparseEnumMessage message = + SparseEnumMessage.newBuilder().setSparseEnum(TestSparseEnum.SPARSE_E).build(); assertTrue(message.getSparseEnum().getNumber() < 0); - byte[] rawBytes = message.toByteArray(); - SparseEnumMessage message2 = SparseEnumMessage.parseFrom(rawBytes); - assertEquals(TestSparseEnum.SPARSE_E, message2.getSparseEnum()); + for (OutputType outputType : OutputType.values()) { + Coder coder = outputType.newCoder(message.getSerializedSize()); + message.writeTo(coder.stream()); + coder.stream().flush(); + byte[] rawBytes = coder.toByteArray(); + SparseEnumMessage message2 = SparseEnumMessage.parseFrom(rawBytes); + assertEquals(TestSparseEnum.SPARSE_E, message2.getSparseEnum()); + } } /** Test getTotalBytesWritten() */ public void testGetTotalBytesWritten() throws Exception { - final int BUFFER_SIZE = 4 * 1024; - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(BUFFER_SIZE); - CodedOutputStream codedStream = CodedOutputStream.newInstance(outputStream); + Coder coder = OutputType.STREAM.newCoder(4 * 1024); + + // Write some some bytes (more than the buffer can hold) and verify that totalWritten + // is correct. byte[] value = "abcde".getBytes(Internal.UTF_8); for (int i = 0; i < 1024; ++i) { - codedStream.writeRawBytes(value, 0, value.length); + coder.stream().writeRawBytes(value, 0, value.length); } + assertEquals(value.length * 1024, coder.stream().getTotalBytesWritten()); + + // Now write an encoded string. String string = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"; // Ensure we take the slower fast path. - assertTrue(CodedOutputStream.computeRawVarint32Size(string.length()) - != CodedOutputStream.computeRawVarint32Size(string.length() * Utf8.MAX_BYTES_PER_CHAR)); - - codedStream.writeStringNoTag(string); + assertTrue(CodedOutputStream.computeUInt32SizeNoTag(string.length()) + != CodedOutputStream.computeUInt32SizeNoTag(string.length() * Utf8.MAX_BYTES_PER_CHAR)); + + coder.stream().writeStringNoTag(string); int stringSize = CodedOutputStream.computeStringSizeNoTag(string); - - // Make sure we have written more bytes than the buffer could hold. This is - // to make the test complete. - assertTrue(codedStream.getTotalBytesWritten() > BUFFER_SIZE); - + // Verify that the total bytes written is correct - assertEquals((value.length * 1024) + stringSize, codedStream.getTotalBytesWritten()); + assertEquals((value.length * 1024) + stringSize, coder.stream().getTotalBytesWritten()); } - + // TODO(dweis): Write a comprehensive test suite for CodedOutputStream that covers more than just // this case. public void testWriteStringNoTag_fastpath() throws Exception { @@ -390,14 +405,16 @@ public class CodedOutputStreamTest extends TestCase { string += threeBytesPer; } // These checks ensure we will tickle the slower fast path. - assertEquals(1, CodedOutputStream.computeRawVarint32Size(string.length())); + assertEquals(1, CodedOutputStream.computeUInt32SizeNoTag(string.length())); assertEquals( - 2, CodedOutputStream.computeRawVarint32Size(string.length() * Utf8.MAX_BYTES_PER_CHAR)); + 2, CodedOutputStream.computeUInt32SizeNoTag(string.length() * Utf8.MAX_BYTES_PER_CHAR)); assertEquals(bufferSize, string.length() * Utf8.MAX_BYTES_PER_CHAR); - - CodedOutputStream output = - CodedOutputStream.newInstance(ByteBuffer.allocate(bufferSize), bufferSize); - output.writeStringNoTag(string); + + for (OutputType outputType : OutputType.values()) { + Coder coder = outputType.newCoder(bufferSize + 2); + coder.stream().writeStringNoTag(string); + coder.stream().flush(); + } } public void testWriteToByteBuffer() throws Exception { @@ -464,83 +481,296 @@ public class CodedOutputStreamTest extends TestCase { byte[] destination = new byte[4]; CodedOutputStream codedStream = CodedOutputStream.newInstance(destination); codedStream.writeByteArrayNoTag(fullArray, 2, 2); - assertEqualBytes(bytes(0x02, 0x33, 0x44, 0x00), destination); + assertEqualBytes(OutputType.ARRAY, bytes(0x02, 0x33, 0x44, 0x00), destination); assertEquals(3, codedStream.getTotalBytesWritten()); } - + + public void testSerializeUtf8_MultipleSmallWrites() throws Exception { + final String source = "abcdefghijklmnopqrstuvwxyz"; + + // Generate the expected output if the source string is written 2 bytes at a time. + ByteArrayOutputStream expectedBytesStream = new ByteArrayOutputStream(); + for (int pos = 0; pos < source.length(); pos += 2) { + String substr = source.substring(pos, pos + 2); + expectedBytesStream.write(2); + expectedBytesStream.write(substr.getBytes(Internal.UTF_8)); + } + final byte[] expectedBytes = expectedBytesStream.toByteArray(); + + // For each output type, write the source string 2 bytes at a time and verify the output. + for (OutputType outputType : OutputType.values()) { + Coder coder = outputType.newCoder(expectedBytes.length); + for (int pos = 0; pos < source.length(); pos += 2) { + String substr = source.substring(pos, pos + 2); + coder.stream().writeStringNoTag(substr); + } + coder.stream().flush(); + assertEqualBytes(outputType, expectedBytes, coder.toByteArray()); + } + } + public void testSerializeInvalidUtf8() throws Exception { - String[] invalidStrings = new String[] { - newString(Character.MIN_HIGH_SURROGATE), - "foobar" + newString(Character.MIN_HIGH_SURROGATE), - newString(Character.MIN_LOW_SURROGATE), + String[] invalidStrings = new String[] {newString(Character.MIN_HIGH_SURROGATE), + "foobar" + newString(Character.MIN_HIGH_SURROGATE), newString(Character.MIN_LOW_SURROGATE), "foobar" + newString(Character.MIN_LOW_SURROGATE), - newString(Character.MIN_HIGH_SURROGATE, Character.MIN_HIGH_SURROGATE) - }; - + newString(Character.MIN_HIGH_SURROGATE, Character.MIN_HIGH_SURROGATE)}; + CodedOutputStream outputWithStream = CodedOutputStream.newInstance(new ByteArrayOutputStream()); CodedOutputStream outputWithArray = CodedOutputStream.newInstance(new byte[10000]); + CodedOutputStream outputWithByteBuffer = + CodedOutputStream.newInstance(ByteBuffer.allocate(10000)); for (String s : invalidStrings) { // TODO(dweis): These should all fail; instead they are corrupting data. CodedOutputStream.computeStringSizeNoTag(s); outputWithStream.writeStringNoTag(s); outputWithArray.writeStringNoTag(s); + outputWithByteBuffer.writeStringNoTag(s); } } - - private static String newString(char... chars) { - return new String(chars); + + // TODO(nathanmittler): This test can be deleted once we properly throw IOException while + // encoding invalid UTF-8 strings. + public void testSerializeInvalidUtf8FollowedByOutOfSpace() throws Exception { + final int notEnoughBytes = 4; + CodedOutputStream outputWithArray = CodedOutputStream.newInstance(new byte[notEnoughBytes]); + CodedOutputStream outputWithByteBuffer = + CodedOutputStream.newInstance(ByteBuffer.allocate(notEnoughBytes)); + + String invalidString = newString(Character.MIN_HIGH_SURROGATE, 'f', 'o', 'o', 'b', 'a', 'r'); + try { + outputWithArray.writeStringNoTag(invalidString); + fail("Expected OutOfSpaceException"); + } catch (OutOfSpaceException e) { + assertTrue(e.getCause() instanceof IndexOutOfBoundsException); + } + try { + outputWithByteBuffer.writeStringNoTag(invalidString); + fail("Expected OutOfSpaceException"); + } catch (OutOfSpaceException e) { + assertTrue(e.getCause() instanceof IndexOutOfBoundsException); + } } /** Regression test for https://github.com/google/protobuf/issues/292 */ public void testCorrectExceptionThrowWhenEncodingStringsWithoutEnoughSpace() throws Exception { String testCase = "Foooooooo"; - assertEquals(CodedOutputStream.computeRawVarint32Size(testCase.length()), - CodedOutputStream.computeRawVarint32Size(testCase.length() * 3)); + assertEquals( + CodedOutputStream.computeUInt32SizeNoTag(testCase.length()), + CodedOutputStream.computeUInt32SizeNoTag(testCase.length() * 3)); assertEquals(11, CodedOutputStream.computeStringSize(1, testCase)); // Tag is one byte, varint describing string length is 1 byte, string length is 9 bytes. // An array of size 1 will cause a failure when trying to write the varint. - for (int i = 0; i < 11; i++) { - CodedOutputStream output = CodedOutputStream.newInstance(new byte[i]); - try { - output.writeString(1, testCase); - fail("Should have thrown an out of space exception"); - } catch (CodedOutputStream.OutOfSpaceException expected) {} + for (OutputType outputType : + new OutputType[] {OutputType.ARRAY, OutputType.NIO_HEAP, OutputType.NIO_DIRECT}) { + for (int i = 0; i < 11; i++) { + Coder coder = outputType.newCoder(i); + try { + coder.stream().writeString(1, testCase); + fail("Should have thrown an out of space exception"); + } catch (CodedOutputStream.OutOfSpaceException expected) { + } + } } } - + public void testDifferentStringLengths() throws Exception { // Test string serialization roundtrip using strings of the following lengths, // with ASCII and Unicode characters requiring different UTF-8 byte counts per // char, hence causing the length delimiter varint to sometimes require more // bytes for the Unicode strings than the ASCII string of the same length. int[] lengths = new int[] { - 0, - 1, - (1 << 4) - 1, // 1 byte for ASCII and Unicode - (1 << 7) - 1, // 1 byte for ASCII, 2 bytes for Unicode - (1 << 11) - 1, // 2 bytes for ASCII and Unicode - (1 << 14) - 1, // 2 bytes for ASCII, 3 bytes for Unicode - (1 << 17) - 1, // 3 bytes for ASCII and Unicode + 0, + 1, + (1 << 4) - 1, // 1 byte for ASCII and Unicode + (1 << 7) - 1, // 1 byte for ASCII, 2 bytes for Unicode + (1 << 11) - 1, // 2 bytes for ASCII and Unicode + (1 << 14) - 1, // 2 bytes for ASCII, 3 bytes for Unicode + (1 << 17) - 1, + // 3 bytes for ASCII and Unicode }; - for (int i : lengths) { - testEncodingOfString('q', i); // 1 byte per char - testEncodingOfString('\u07FF', i); // 2 bytes per char - testEncodingOfString('\u0981', i); // 3 bytes per char + for (OutputType outputType : OutputType.values()) { + for (int i : lengths) { + testEncodingOfString(outputType, 'q', i); // 1 byte per char + testEncodingOfString(outputType, '\u07FF', i); // 2 bytes per char + testEncodingOfString(outputType, '\u0981', i); // 3 bytes per char + } } } - private void testEncodingOfString(char c, int length) throws Exception { - String fullString = fullString(c, length); - TestAllTypes testAllTypes = TestAllTypes.newBuilder() - .setOptionalString(fullString) - .build(); - assertEquals( - fullString, TestAllTypes.parseFrom(testAllTypes.toByteArray()).getOptionalString()); + public void testNioEncodersWithInitialOffsets() throws Exception { + String value = "abc"; + for (Coder coder : new Coder[] {new NioHeapCoder(10, 2), new NioDirectCoder(10, 2)}) { + coder.stream().writeStringNoTag(value); + coder.stream().flush(); + assertEqualBytes(coder.getOutputType(), new byte[]{3, 'a', 'b', 'c'}, coder.toByteArray()); + } } - private String fullString(char c, int length) { + /** + * Parses the given bytes using writeRawLittleEndian32() and checks + * that the result matches the given value. + */ + private static void assertWriteLittleEndian32(byte[] data, int value) throws Exception { + for (OutputType outputType : OutputType.values()) { + Coder coder = outputType.newCoder(data.length); + coder.stream().writeFixed32NoTag(value); + coder.stream().flush(); + assertEqualBytes(outputType, data, coder.toByteArray()); + } + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + Coder coder = OutputType.STREAM.newCoder(blockSize); + coder.stream().writeFixed32NoTag(value); + coder.stream().flush(); + assertEqualBytes(OutputType.STREAM, data, coder.toByteArray()); + } + } + + /** + * Parses the given bytes using writeRawLittleEndian64() and checks + * that the result matches the given value. + */ + private static void assertWriteLittleEndian64(byte[] data, long value) throws Exception { + for (OutputType outputType : OutputType.values()) { + Coder coder = outputType.newCoder(data.length); + coder.stream().writeFixed64NoTag(value); + coder.stream().flush(); + assertEqualBytes(outputType, data, coder.toByteArray()); + } + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + Coder coder = OutputType.STREAM.newCoder(blockSize); + coder.stream().writeFixed64NoTag(value); + coder.stream().flush(); + assertEqualBytes(OutputType.STREAM, data, coder.toByteArray()); + } + } + + private static String newString(char... chars) { + return new String(chars); + } + + private static void testEncodingOfString(OutputType outputType, char c, int length) + throws Exception { + String fullString = fullString(c, length); + TestAllTypes testAllTypes = TestAllTypes.newBuilder().setOptionalString(fullString).build(); + Coder coder = outputType.newCoder(testAllTypes.getSerializedSize()); + testAllTypes.writeTo(coder.stream()); + coder.stream().flush(); + assertEquals( + "OuputType: " + outputType, + fullString, + TestAllTypes.parseFrom(coder.toByteArray()).getOptionalString()); + } + + private static String fullString(char c, int length) { char[] result = new char[length]; Arrays.fill(result, c); return new String(result); } + + /** + * 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. + */ + private static byte[] bytes(int... bytesAsInts) { + byte[] bytes = new byte[bytesAsInts.length]; + for (int i = 0; i < bytesAsInts.length; i++) { + bytes[i] = (byte) bytesAsInts[i]; + } + return bytes; + } + + /** Arrays.asList() does not work with arrays of primitives. :( */ + private static List toList(byte[] bytes) { + List result = new ArrayList(); + for (byte b : bytes) { + result.add(b); + } + return result; + } + + private static void assertEqualBytes(OutputType outputType, byte[] a, byte[] b) { + assertEquals(outputType.name(), toList(a), toList(b)); + } + + /** + * Writes the given value using writeRawVarint32() and writeRawVarint64() and + * checks that the result matches the given bytes. + */ + private static void assertWriteVarint(byte[] data, long value) throws Exception { + for (OutputType outputType : OutputType.values()) { + // Only test 32-bit write if the value fits into an int. + if (value == (int) value) { + Coder coder = outputType.newCoder(10); + coder.stream().writeUInt32NoTag((int) value); + coder.stream().flush(); + assertEqualBytes(outputType, data, coder.toByteArray()); + + // Also try computing size. + assertEquals(data.length, CodedOutputStream.computeUInt32SizeNoTag((int) value)); + } + + { + Coder coder = outputType.newCoder(10); + coder.stream().writeUInt64NoTag(value); + coder.stream().flush(); + assertEqualBytes(outputType, data, coder.toByteArray()); + + // Also try computing size. + assertEquals(data.length, CodedOutputStream.computeUInt64SizeNoTag(value)); + } + } + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + // Only test 32-bit write if the value fits into an int. + if (value == (int) value) { + Coder coder = OutputType.STREAM.newCoder(blockSize); + coder.stream().writeUInt64NoTag((int) value); + coder.stream().flush(); + assertEqualBytes(OutputType.STREAM, data, coder.toByteArray()); + + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, blockSize); + output.writeUInt32NoTag((int) value); + output.flush(); + assertEqualBytes(OutputType.STREAM, data, rawOutput.toByteArray()); + } + + { + Coder coder = OutputType.STREAM.newCoder(blockSize); + coder.stream().writeUInt64NoTag(value); + coder.stream().flush(); + assertEqualBytes(OutputType.STREAM, data, coder.toByteArray()); + } + } + } + + private static void assertVarintRoundTrip(OutputType outputType, long value) throws Exception { + { + Coder coder = outputType.newCoder(10); + coder.stream().writeUInt64NoTag(value); + coder.stream().flush(); + byte[] bytes = coder.toByteArray(); + assertEquals( + outputType.name(), bytes.length, CodedOutputStream.computeUInt64SizeNoTag(value)); + CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(bytes)); + assertEquals(outputType.name(), value, input.readRawVarint64()); + } + + if (value == (int) value) { + Coder coder = outputType.newCoder(10); + coder.stream().writeUInt32NoTag((int) value); + coder.stream().flush(); + byte[] bytes = coder.toByteArray(); + assertEquals( + outputType.name(), bytes.length, CodedOutputStream.computeUInt32SizeNoTag((int) value)); + CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(bytes)); + assertEquals(outputType.name(), value, input.readRawVarint32()); + } + } } diff --git a/java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java b/java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java index e7905f79d..ce85ae2ed 100644 --- a/java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java +++ b/java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java @@ -36,6 +36,7 @@ import junit.framework.TestCase; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; + /** * Test field deprecation * diff --git a/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java index 82ff34afe..ef89b389c 100644 --- a/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java +++ b/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java @@ -59,7 +59,6 @@ import protobuf_unittest.UnittestProto.TestMultipleExtensionRanges; import protobuf_unittest.UnittestProto.TestRequired; import protobuf_unittest.UnittestProto.TestReservedFields; import protobuf_unittest.UnittestProto.TestService; - import junit.framework.TestCase; import java.util.Arrays; @@ -573,6 +572,42 @@ public class DescriptorsTest extends TestCase { } } + public void testUnknownFieldsDenied() throws Exception { + FileDescriptorProto fooProto = FileDescriptorProto.newBuilder() + .setName("foo.proto") + .addMessageType(DescriptorProto.newBuilder() + .setName("Foo") + .addField(FieldDescriptorProto.newBuilder() + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setTypeName("Bar") + .setName("bar") + .setNumber(1))) + .build(); + + try { + Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0]); + fail("DescriptorValidationException expected"); + } catch (DescriptorValidationException e) { + assertTrue(e.getMessage().indexOf("Bar") != -1); + assertTrue(e.getMessage().indexOf("is not defined") != -1); + } + } + + public void testUnknownFieldsAllowed() throws Exception { + FileDescriptorProto fooProto = FileDescriptorProto.newBuilder() + .setName("foo.proto") + .addDependency("bar.proto") + .addMessageType(DescriptorProto.newBuilder() + .setName("Foo") + .addField(FieldDescriptorProto.newBuilder() + .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL) + .setTypeName("Bar") + .setName("bar") + .setNumber(1))) + .build(); + Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0], true); + } + public void testHiddenDependency() throws Exception { FileDescriptorProto barProto = FileDescriptorProto.newBuilder() .setName("bar.proto") diff --git a/java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java b/java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java index d3deaa073..85b418c44 100644 --- a/java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java +++ b/java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java @@ -73,20 +73,6 @@ public class DoubleArrayListTest extends TestCase { assertImmutable(list); } - public void testCopyConstructor() { - DoubleArrayList copy = new DoubleArrayList(TERTIARY_LIST); - assertEquals(TERTIARY_LIST, copy); - - copy = new DoubleArrayList(DoubleArrayList.emptyList()); - assertEquals(DoubleArrayList.emptyList(), copy); - - copy = new DoubleArrayList(asList(1D, 2D, 3D)); - assertEquals(asList(1D, 2D, 3D), copy); - - copy = new DoubleArrayList(Collections.emptyList()); - assertEquals(DoubleArrayList.emptyList(), copy); - } - public void testModificationWithIteration() { list.addAll(asList(1D, 2D, 3D, 4D)); Iterator iterator = list.iterator(); diff --git a/java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java b/java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java index 55144e7c0..8f3a74c15 100644 --- a/java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java +++ b/java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java @@ -33,13 +33,13 @@ package com.google.protobuf; import com.google.protobuf.Descriptors.EnumDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.OneofDescriptor; - import protobuf_unittest.UnittestProto.TestAllExtensions; import protobuf_unittest.UnittestProto.TestAllTypes; import protobuf_unittest.UnittestProto.TestEmptyMessage; import protobuf_unittest.UnittestProto.TestPackedTypes; import junit.framework.TestCase; + import java.util.Arrays; /** diff --git a/java/core/src/test/java/com/google/protobuf/EnumTest.java b/java/core/src/test/java/com/google/protobuf/EnumTest.java new file mode 100644 index 000000000..14c7406b3 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/EnumTest.java @@ -0,0 +1,76 @@ +// 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. + +package com.google.protobuf; + +import com.google.protobuf.UnittestLite.ForeignEnumLite; +import com.google.protobuf.UnittestLite.TestAllTypesLite; +import protobuf_unittest.UnittestProto.ForeignEnum; +import protobuf_unittest.UnittestProto.TestAllTypes; + +import junit.framework.TestCase; + +public class EnumTest extends TestCase { + + public void testForNumber() { + ForeignEnum e = ForeignEnum.forNumber(ForeignEnum.FOREIGN_BAR.getNumber()); + assertEquals(ForeignEnum.FOREIGN_BAR, e); + + e = ForeignEnum.forNumber(1000); + assertEquals(null, e); + } + + public void testForNumber_oneof() { + TestAllTypes.OneofFieldCase e = TestAllTypes.OneofFieldCase.forNumber( + TestAllTypes.OneofFieldCase.ONEOF_NESTED_MESSAGE.getNumber()); + assertEquals(TestAllTypes.OneofFieldCase.ONEOF_NESTED_MESSAGE, e); + + e = TestAllTypes.OneofFieldCase.forNumber(1000); + assertEquals(null, e); + } + + public void testForNumberLite() { + ForeignEnumLite e = ForeignEnumLite.forNumber(ForeignEnumLite.FOREIGN_LITE_BAR.getNumber()); + assertEquals(ForeignEnumLite.FOREIGN_LITE_BAR, e); + + e = ForeignEnumLite.forNumber(1000); + assertEquals(null, e); + } + + public void testForNumberLite_oneof() { + TestAllTypesLite.OneofFieldCase e = TestAllTypesLite.OneofFieldCase.forNumber( + TestAllTypesLite.OneofFieldCase.ONEOF_NESTED_MESSAGE.getNumber()); + assertEquals(TestAllTypesLite.OneofFieldCase.ONEOF_NESTED_MESSAGE, e); + + e = TestAllTypesLite.OneofFieldCase.forNumber(1000); + assertEquals(null, e); + } +} + diff --git a/java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java b/java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java index eaeec0b8a..304cec4f8 100644 --- a/java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java +++ b/java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java @@ -44,9 +44,9 @@ import junit.framework.TestCase; * non-message fields. */ public class FieldPresenceTest extends TestCase { - private static boolean hasMethod(Class clazz, String name) { + private static boolean hasMethod(Class clazz, String name) { try { - if (clazz.getMethod(name, new Class[]{}) != null) { + if (clazz.getMethod(name) != null) { return true; } else { return false; @@ -56,90 +56,90 @@ public class FieldPresenceTest extends TestCase { } } - private static boolean isHasMethodRemoved( - Class classWithFieldPresence, - Class classWithoutFieldPresence, + private static void assertHasMethodRemoved( + Class classWithFieldPresence, + Class classWithoutFieldPresence, String camelName) { - return hasMethod(classWithFieldPresence, "get" + camelName) - && hasMethod(classWithFieldPresence, "has" + camelName) - && hasMethod(classWithoutFieldPresence, "get" + camelName) - && !hasMethod(classWithoutFieldPresence, "has" + camelName); + assertTrue(hasMethod(classWithFieldPresence, "get" + camelName)); + assertTrue(hasMethod(classWithFieldPresence, "has" + camelName)); + assertTrue(hasMethod(classWithoutFieldPresence, "get" + camelName)); + assertFalse(hasMethod(classWithoutFieldPresence, "has" + camelName)); } public void testHasMethod() { // Optional non-message fields don't have a hasFoo() method generated. - assertTrue(isHasMethodRemoved( + assertHasMethodRemoved( UnittestProto.TestAllTypes.class, TestAllTypes.class, - "OptionalInt32")); - assertTrue(isHasMethodRemoved( + "OptionalInt32"); + assertHasMethodRemoved( UnittestProto.TestAllTypes.class, TestAllTypes.class, - "OptionalString")); - assertTrue(isHasMethodRemoved( + "OptionalString"); + assertHasMethodRemoved( UnittestProto.TestAllTypes.class, TestAllTypes.class, - "OptionalBytes")); - assertTrue(isHasMethodRemoved( + "OptionalBytes"); + assertHasMethodRemoved( UnittestProto.TestAllTypes.class, TestAllTypes.class, - "OptionalNestedEnum")); + "OptionalNestedEnum"); - assertTrue(isHasMethodRemoved( + assertHasMethodRemoved( UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, - "OptionalInt32")); - assertTrue(isHasMethodRemoved( + "OptionalInt32"); + assertHasMethodRemoved( UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, - "OptionalString")); - assertTrue(isHasMethodRemoved( + "OptionalString"); + assertHasMethodRemoved( UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, - "OptionalBytes")); - assertTrue(isHasMethodRemoved( + "OptionalBytes"); + assertHasMethodRemoved( UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, - "OptionalNestedEnum")); + "OptionalNestedEnum"); // message fields still have the hasFoo() method generated. assertFalse(TestAllTypes.newBuilder().build().hasOptionalNestedMessage()); assertFalse(TestAllTypes.newBuilder().hasOptionalNestedMessage()); // oneof fields don't have hasFoo() methods (even for message types). - assertTrue(isHasMethodRemoved( + assertHasMethodRemoved( UnittestProto.TestAllTypes.class, TestAllTypes.class, - "OneofUint32")); - assertTrue(isHasMethodRemoved( + "OneofUint32"); + assertHasMethodRemoved( UnittestProto.TestAllTypes.class, TestAllTypes.class, - "OneofString")); - assertTrue(isHasMethodRemoved( + "OneofString"); + assertHasMethodRemoved( UnittestProto.TestAllTypes.class, TestAllTypes.class, - "OneofBytes")); - assertTrue(isHasMethodRemoved( + "OneofBytes"); + assertHasMethodRemoved( UnittestProto.TestAllTypes.class, TestAllTypes.class, - "OneofNestedMessage")); + "OneofNestedMessage"); - assertTrue(isHasMethodRemoved( + assertHasMethodRemoved( UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, - "OneofUint32")); - assertTrue(isHasMethodRemoved( + "OneofUint32"); + assertHasMethodRemoved( UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, - "OneofString")); - assertTrue(isHasMethodRemoved( + "OneofString"); + assertHasMethodRemoved( UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, - "OneofBytes")); - assertTrue(isHasMethodRemoved( + "OneofBytes"); + assertHasMethodRemoved( UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, - "OneofNestedMessage")); + "OneofNestedMessage"); } public void testOneofEquals() throws Exception { @@ -232,24 +232,24 @@ public class FieldPresenceTest extends TestCase { assertTrue(message.hasField(optionalNestedEnumField)); assertEquals(4, message.getAllFields().size()); } - + public void testMessageField() { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); assertFalse(builder.hasOptionalNestedMessage()); assertFalse(builder.build().hasOptionalNestedMessage()); - + TestAllTypes.NestedMessage.Builder nestedBuilder = builder.getOptionalNestedMessageBuilder(); assertTrue(builder.hasOptionalNestedMessage()); assertTrue(builder.build().hasOptionalNestedMessage()); - + nestedBuilder.setValue(1); assertEquals(1, builder.build().getOptionalNestedMessage().getValue()); - + builder.clearOptionalNestedMessage(); assertFalse(builder.hasOptionalNestedMessage()); assertFalse(builder.build().hasOptionalNestedMessage()); - + // Unlike non-message fields, if we set a message field to its default value (i.e., // default instance), the field should be seen as present. builder.setOptionalNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance()); @@ -340,7 +340,7 @@ public class FieldPresenceTest extends TestCase { assertTrue(builder.buildPartial().isInitialized()); } - + // Test that unknown fields are dropped. public void testUnknownFields() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); @@ -348,7 +348,7 @@ public class FieldPresenceTest extends TestCase { builder.addRepeatedInt32(5678); TestAllTypes message = builder.build(); ByteString data = message.toByteString(); - + TestOptionalFieldsOnly optionalOnlyMessage = TestOptionalFieldsOnly.parseFrom(data); // UnknownFieldSet should be empty. @@ -360,7 +360,7 @@ public class FieldPresenceTest extends TestCase { // The repeated field is discarded because it's unknown to the optional-only // message. assertEquals(0, message.getRepeatedInt32Count()); - + DynamicMessage dynamicOptionalOnlyMessage = DynamicMessage.getDefaultInstance( TestOptionalFieldsOnly.getDescriptor()) diff --git a/java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java b/java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java index a5e654244..88a757431 100644 --- a/java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java +++ b/java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java @@ -73,20 +73,6 @@ public class FloatArrayListTest extends TestCase { assertImmutable(list); } - public void testCopyConstructor() { - FloatArrayList copy = new FloatArrayList(TERTIARY_LIST); - assertEquals(TERTIARY_LIST, copy); - - copy = new FloatArrayList(FloatArrayList.emptyList()); - assertEquals(FloatArrayList.emptyList(), copy); - - copy = new FloatArrayList(asList(1F, 2F, 3F)); - assertEquals(asList(1F, 2F, 3F), copy); - - copy = new FloatArrayList(Collections.emptyList()); - assertEquals(FloatArrayList.emptyList(), copy); - } - public void testModificationWithIteration() { list.addAll(asList(1F, 2F, 3F, 4F)); Iterator iterator = list.iterator(); diff --git a/java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java b/java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java index a92ba374a..b7eaebf5e 100644 --- a/java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java +++ b/java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java @@ -41,7 +41,7 @@ package com.google.protobuf; */ public class ForceFieldBuildersPreRun implements Runnable { - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void run() { GeneratedMessage.enableAlwaysUseFieldBuildersForTesting(); } 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 70812b953..a9b8b6381 100644 --- a/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java +++ b/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java @@ -620,6 +620,21 @@ public class GeneratedMessageTest extends TestCase { TestUtil.assertExtensionsClear(TestAllExtensions.newBuilder().build()); } + public void testUnsetRepeatedExtensionGetField() { + TestAllExtensions message = TestAllExtensions.getDefaultInstance(); + Object value; + + value = message.getField(UnittestProto.repeatedStringExtension.getDescriptor()); + assertTrue(value instanceof List); + assertTrue(((List) value).isEmpty()); + assertIsUnmodifiable((List) value); + + value = message.getField(UnittestProto.repeatedNestedMessageExtension.getDescriptor()); + assertTrue(value instanceof List); + assertTrue(((List) value).isEmpty()); + assertIsUnmodifiable((List) value); + } + public void testExtensionReflectionGetters() throws Exception { TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); TestUtil.setAllExtensions(builder); @@ -708,7 +723,7 @@ public class GeneratedMessageTest extends TestCase { public void testLiteExtensionMessageOrBuilder() throws Exception { TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder(); - TestUtil.setAllExtensions(builder); + TestUtilLite.setAllExtensions(builder); TestUtil.assertAllExtensionsSet(builder); TestAllExtensionsLite message = builder.build(); @@ -717,8 +732,8 @@ public class GeneratedMessageTest extends TestCase { public void testLiteExtensionRepeatedSetters() throws Exception { TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder(); - TestUtil.setAllExtensions(builder); - TestUtil.modifyRepeatedExtensions(builder); + TestUtilLite.setAllExtensions(builder); + TestUtilLite.modifyRepeatedExtensions(builder); TestUtil.assertRepeatedExtensionsModified(builder); TestAllExtensionsLite message = builder.build(); @@ -745,7 +760,7 @@ public class GeneratedMessageTest extends TestCase { } public void testLiteExtensionCopy() throws Exception { - TestAllExtensionsLite original = TestUtil.getAllLiteExtensionsSet(); + TestAllExtensionsLite original = TestUtilLite.getAllLiteExtensionsSet(); TestAllExtensionsLite copy = TestAllExtensionsLite.newBuilder(original).build(); TestUtil.assertAllExtensionsSet(copy); diff --git a/java/core/src/test/java/com/google/protobuf/IntArrayListTest.java b/java/core/src/test/java/com/google/protobuf/IntArrayListTest.java index 3733eb305..efb8f3e20 100644 --- a/java/core/src/test/java/com/google/protobuf/IntArrayListTest.java +++ b/java/core/src/test/java/com/google/protobuf/IntArrayListTest.java @@ -72,20 +72,6 @@ public class IntArrayListTest extends TestCase { list.makeImmutable(); assertImmutable(list); } - - public void testCopyConstructor() { - IntArrayList copy = new IntArrayList(TERTIARY_LIST); - assertEquals(TERTIARY_LIST, copy); - - copy = new IntArrayList(IntArrayList.emptyList()); - assertEquals(IntArrayList.emptyList(), copy); - - copy = new IntArrayList(asList(1, 2, 3)); - assertEquals(asList(1, 2, 3), copy); - - copy = new IntArrayList(Collections.emptyList()); - assertEquals(IntArrayList.emptyList(), copy); - } public void testModificationWithIteration() { list.addAll(asList(1, 2, 3, 4)); diff --git a/java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java b/java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java index 8751baaea..756049b41 100644 --- a/java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java +++ b/java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java @@ -30,12 +30,18 @@ package com.google.protobuf; +import static com.google.protobuf.IsValidUtf8TestUtil.DIRECT_NIO_FACTORY; +import static com.google.protobuf.IsValidUtf8TestUtil.EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT; +import static com.google.protobuf.IsValidUtf8TestUtil.EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT; +import static com.google.protobuf.IsValidUtf8TestUtil.HEAP_NIO_FACTORY; +import static com.google.protobuf.IsValidUtf8TestUtil.LITERAL_FACTORY; +import static com.google.protobuf.IsValidUtf8TestUtil.testBytes; + +import com.google.protobuf.IsValidUtf8TestUtil.ByteStringFactory; import com.google.protobuf.IsValidUtf8TestUtil.Shard; import junit.framework.TestCase; -import java.io.UnsupportedEncodingException; - /** * Tests cases for {@link ByteString#isValidUtf8()}. This includes three * brute force tests that actually test every permutation of one byte, two byte, @@ -51,31 +57,33 @@ import java.io.UnsupportedEncodingException; * @author martinrb@google.com (Martin Buchholz) */ public class IsValidUtf8Test extends TestCase { - /** * Tests that round tripping of all two byte permutations work. */ - public void testIsValidUtf8_1Byte() throws UnsupportedEncodingException { - IsValidUtf8TestUtil.testBytes(1, - IsValidUtf8TestUtil.EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT); + public void testIsValidUtf8_1Byte() { + testBytes(LITERAL_FACTORY, 1, EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT); + testBytes(HEAP_NIO_FACTORY, 1, EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT); + testBytes(DIRECT_NIO_FACTORY, 1, EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT); } /** * Tests that round tripping of all two byte permutations work. */ - public void testIsValidUtf8_2Bytes() throws UnsupportedEncodingException { - IsValidUtf8TestUtil.testBytes(2, - IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT); + public void testIsValidUtf8_2Bytes() { + testBytes(LITERAL_FACTORY, 2, IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT); + testBytes(HEAP_NIO_FACTORY, 2, IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT); + testBytes(DIRECT_NIO_FACTORY, 2, IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT); } /** * Tests that round tripping of all three byte permutations work. */ - public void testIsValidUtf8_3Bytes() throws UnsupportedEncodingException { + public void testIsValidUtf8_3Bytes() { // Travis' OOM killer doesn't like this test if (System.getenv("TRAVIS") == null) { - IsValidUtf8TestUtil.testBytes(3, - IsValidUtf8TestUtil.EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT); + testBytes(LITERAL_FACTORY, 3, EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT); + testBytes(HEAP_NIO_FACTORY, 3, EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT); + testBytes(DIRECT_NIO_FACTORY, 3, EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT); } } @@ -85,8 +93,7 @@ public class IsValidUtf8Test extends TestCase { * {@link IsValidUtf8FourByteTest} is used for full coverage. This method * tests specific four-byte cases. */ - public void testIsValidUtf8_4BytesSamples() - throws UnsupportedEncodingException { + public void testIsValidUtf8_4BytesSamples() { // Valid 4 byte. assertValidUtf8(0xF0, 0xA4, 0xAD, 0xA2); @@ -119,9 +126,7 @@ public class IsValidUtf8Test extends TestCase { assertTrue(asBytes("\u024B62\u024B62").isValidUtf8()); // Mixed string - assertTrue( - asBytes("a\u020ac\u00a2b\\u024B62u020acc\u00a2de\u024B62") - .isValidUtf8()); + assertTrue(asBytes("a\u020ac\u00a2b\\u024B62u020acc\u00a2de\u024B62").isValidUtf8()); // Not a valid string assertInvalidUtf8(-1, 0, -1, 0); @@ -135,36 +140,35 @@ public class IsValidUtf8Test extends TestCase { return realBytes; } - private ByteString toByteString(int... bytes) { - return ByteString.copyFrom(toByteArray(bytes)); - } - - private void assertValidUtf8(int[] bytes, boolean not) { + private void assertValidUtf8(ByteStringFactory factory, int[] bytes, boolean not) { byte[] realBytes = toByteArray(bytes); assertTrue(not ^ Utf8.isValidUtf8(realBytes)); assertTrue(not ^ Utf8.isValidUtf8(realBytes, 0, bytes.length)); - ByteString lit = ByteString.copyFrom(realBytes); - ByteString sub = lit.substring(0, bytes.length); - assertTrue(not ^ lit.isValidUtf8()); + ByteString leaf = factory.newByteString(realBytes); + ByteString sub = leaf.substring(0, bytes.length); + assertTrue(not ^ leaf.isValidUtf8()); assertTrue(not ^ sub.isValidUtf8()); ByteString[] ropes = { - RopeByteString.newInstanceForTest(ByteString.EMPTY, lit), - RopeByteString.newInstanceForTest(ByteString.EMPTY, sub), - RopeByteString.newInstanceForTest(lit, ByteString.EMPTY), - RopeByteString.newInstanceForTest(sub, ByteString.EMPTY), - RopeByteString.newInstanceForTest(sub, lit) - }; + RopeByteString.newInstanceForTest(ByteString.EMPTY, leaf), + RopeByteString.newInstanceForTest(ByteString.EMPTY, sub), + RopeByteString.newInstanceForTest(leaf, ByteString.EMPTY), + RopeByteString.newInstanceForTest(sub, ByteString.EMPTY), + RopeByteString.newInstanceForTest(sub, leaf)}; for (ByteString rope : ropes) { assertTrue(not ^ rope.isValidUtf8()); } } private void assertValidUtf8(int... bytes) { - assertValidUtf8(bytes, false); + assertValidUtf8(LITERAL_FACTORY, bytes, false); + assertValidUtf8(HEAP_NIO_FACTORY, bytes, false); + assertValidUtf8(DIRECT_NIO_FACTORY, bytes, false); } private void assertInvalidUtf8(int... bytes) { - assertValidUtf8(bytes, true); + assertValidUtf8(LITERAL_FACTORY, bytes, true); + assertValidUtf8(HEAP_NIO_FACTORY, bytes, true); + assertValidUtf8(DIRECT_NIO_FACTORY, bytes, true); } private static ByteString asBytes(String s) { @@ -177,7 +181,6 @@ public class IsValidUtf8Test extends TestCase { for (Shard shard : IsValidUtf8TestUtil.FOUR_BYTE_SHARDS) { actual += shard.expected; } - assertEquals(IsValidUtf8TestUtil.EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT, - actual); + assertEquals(IsValidUtf8TestUtil.EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT, actual); } } diff --git a/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java b/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java index 321669f31..16a808bf3 100644 --- a/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java +++ b/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java @@ -30,9 +30,13 @@ package com.google.protobuf; -import static junit.framework.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; -import java.io.UnsupportedEncodingException; +import java.lang.ref.SoftReference; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharsetDecoder; @@ -52,64 +56,105 @@ import java.util.logging.Logger; * @author jonp@google.com (Jon Perlow) * @author martinrb@google.com (Martin Buchholz) */ -class IsValidUtf8TestUtil { - private static Logger logger = Logger.getLogger( - IsValidUtf8TestUtil.class.getName()); +final class IsValidUtf8TestUtil { + private static Logger logger = Logger.getLogger(IsValidUtf8TestUtil.class.getName()); + + private IsValidUtf8TestUtil() {} + + static interface ByteStringFactory { + ByteString newByteString(byte[] bytes); + } + + static final ByteStringFactory LITERAL_FACTORY = new ByteStringFactory() { + @Override + public ByteString newByteString(byte[] bytes) { + return ByteString.wrap(bytes); + } + }; + + static final ByteStringFactory HEAP_NIO_FACTORY = new ByteStringFactory() { + @Override + public ByteString newByteString(byte[] bytes) { + return new NioByteString(ByteBuffer.wrap(bytes)); + } + }; + + private static ThreadLocal> directBuffer = + new ThreadLocal>(); + + /** + * Factory for direct {@link ByteBuffer} instances. To reduce direct memory usage, this + * uses a thread local direct buffer. This means that each call will overwrite the buffer's + * contents from the previous call, so the calling code must be careful not to continue using + * a buffer returned from a previous invocation. + */ + static final ByteStringFactory DIRECT_NIO_FACTORY = new ByteStringFactory() { + @Override + public ByteString newByteString(byte[] bytes) { + SoftReference ref = directBuffer.get(); + ByteBuffer buffer = ref == null ? null : ref.get(); + if (buffer == null || buffer.capacity() < bytes.length) { + buffer = ByteBuffer.allocateDirect(bytes.length); + directBuffer.set(new SoftReference(buffer)); + } + buffer.clear(); + buffer.put(bytes); + buffer.flip(); + return new NioByteString(buffer); + } + }; // 128 - [chars 0x0000 to 0x007f] - static long ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x007f - 0x0000 + 1; + static final long ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x007f - 0x0000 + 1; // 128 - static long EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT = - ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS; + static final long EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT = ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS; // 1920 [chars 0x0080 to 0x07FF] - static long TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x07FF - 0x0080 + 1; + static final long TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x07FF - 0x0080 + 1; // 18,304 - static long EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT = + static final long EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT = // Both bytes are one byte characters (long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 2) + // The possible number of two byte characters TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS; // 2048 - static long THREE_BYTE_SURROGATES = 2 * 1024; + static final long THREE_BYTE_SURROGATES = 2 * 1024; // 61,440 [chars 0x0800 to 0xFFFF, minus surrogates] - static long THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS = + static final long THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0xFFFF - 0x0800 + 1 - THREE_BYTE_SURROGATES; // 2,650,112 - static long EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT = + static final long EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT = // All one byte characters (long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 3) + // One two byte character and a one byte character - 2 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS * - ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS + - // Three byte characters + 2 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS * ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS + + // Three byte characters THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS; // 1,048,576 [chars 0x10000L to 0x10FFFF] - static long FOUR_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x10FFFF - 0x10000L + 1; + static final long FOUR_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x10FFFF - 0x10000L + 1; // 289,571,839 - static long EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT = + static final long EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT = // All one byte characters (long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 4) + // One and three byte characters - 2 * THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS * - ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS + + 2 * THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS * ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS + // Two two byte characters TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS + // Permutations of one and two byte characters - 3 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS * - ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS * - ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS + + 3 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS * ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS + * ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS + + // Four byte characters FOUR_BYTE_ROUNDTRIPPABLE_CHARACTERS; - static class Shard { + static final class Shard { final long index; final long start; final long lim; @@ -138,7 +183,7 @@ class IsValidUtf8TestUtil { // 97-111 are all 2342912 for (int i = 97; i <= 111; i++) { - expected[i] = 2342912; + expected[i] = 2342912; } // 113-117 are all 1048576 @@ -158,22 +203,18 @@ class IsValidUtf8TestUtil { return expected; } - static final List FOUR_BYTE_SHARDS = generateFourByteShards( - 128, FOUR_BYTE_SHARDS_EXPECTED_ROUNTRIPPABLES); + static final List FOUR_BYTE_SHARDS = + generateFourByteShards(128, FOUR_BYTE_SHARDS_EXPECTED_ROUNTRIPPABLES); - private static List generateFourByteShards( - int numShards, long[] expected) { + private static List generateFourByteShards(int numShards, long[] expected) { assertEquals(numShards, expected.length); List shards = new ArrayList(numShards); long LIM = 1L << 32; long increment = LIM / numShards; assertTrue(LIM % numShards == 0); for (int i = 0; i < numShards; i++) { - shards.add(new Shard(i, - increment * i, - increment * (i + 1), - expected[i])); + shards.add(new Shard(i, increment * i, increment * (i + 1), expected[i])); } return shards; } @@ -182,12 +223,12 @@ class IsValidUtf8TestUtil { * Helper to run the loop to test all the permutations for the number of bytes * specified. * + * @param factory the factory for {@link ByteString} instances. * @param numBytes the number of bytes in the byte array * @param expectedCount the expected number of roundtrippable permutations */ - static void testBytes(int numBytes, long expectedCount) - throws UnsupportedEncodingException { - testBytes(numBytes, expectedCount, 0, -1); + static void testBytes(ByteStringFactory factory, int numBytes, long expectedCount) { + testBytes(factory, numBytes, expectedCount, 0, -1); } /** @@ -195,14 +236,15 @@ class IsValidUtf8TestUtil { * specified. This overload is useful for debugging to get the loop to start * at a certain character. * + * @param factory the factory for {@link ByteString} instances. * @param numBytes the number of bytes in the byte array * @param expectedCount the expected number of roundtrippable permutations * @param start the starting bytes encoded as a long as big-endian * @param lim the limit of bytes to process encoded as a long as big-endian, * or -1 to mean the max limit for numBytes */ - static void testBytes(int numBytes, long expectedCount, long start, long lim) - throws UnsupportedEncodingException { + static void testBytes( + ByteStringFactory factory, int numBytes, long expectedCount, long start, long lim) { Random rnd = new Random(); byte[] bytes = new byte[numBytes]; @@ -217,7 +259,7 @@ class IsValidUtf8TestUtil { bytes[bytes.length - i - 1] = (byte) tmpByteChar; tmpByteChar = tmpByteChar >> 8; } - ByteString bs = ByteString.copyFrom(bytes); + ByteString bs = factory.newByteString(bytes); boolean isRoundTrippable = bs.isValidUtf8(); String s = new String(bytes, Internal.UTF_8); byte[] bytesReencoded = s.getBytes(Internal.UTF_8); @@ -236,14 +278,15 @@ class IsValidUtf8TestUtil { int i = rnd.nextInt(numBytes); int j = rnd.nextInt(numBytes); if (j < i) { - int tmp = i; i = j; j = tmp; + int tmp = i; + i = j; + j = tmp; } int state1 = Utf8.partialIsValidUtf8(Utf8.COMPLETE, bytes, 0, i); int state2 = Utf8.partialIsValidUtf8(state1, bytes, i, j); int state3 = Utf8.partialIsValidUtf8(state2, bytes, j, numBytes); if (isRoundTrippable != (state3 == Utf8.COMPLETE)) { - System.out.printf("state=%04x %04x %04x i=%d j=%d%n", - state1, state2, state3, i, j); + System.out.printf("state=%04x %04x %04x i=%d j=%d%n", state1, state2, state3, i, j); outputFailure(byteChar, bytes, bytesReencoded); } assertEquals(isRoundTrippable, (state3 == Utf8.COMPLETE)); @@ -251,36 +294,24 @@ class IsValidUtf8TestUtil { // Test ropes built out of small partial sequences ByteString rope = RopeByteString.newInstanceForTest( bs.substring(0, i), - RopeByteString.newInstanceForTest( - bs.substring(i, j), - bs.substring(j, numBytes))); + RopeByteString.newInstanceForTest(bs.substring(i, j), bs.substring(j, numBytes))); assertSame(RopeByteString.class, rope.getClass()); - ByteString[] byteStrings = { bs, bs.substring(0, numBytes), rope }; + ByteString[] byteStrings = {bs, bs.substring(0, numBytes), rope}; for (ByteString x : byteStrings) { - assertEquals(isRoundTrippable, - x.isValidUtf8()); - assertEquals(state3, - x.partialIsValidUtf8(Utf8.COMPLETE, 0, numBytes)); + assertEquals(isRoundTrippable, x.isValidUtf8()); + assertEquals(state3, x.partialIsValidUtf8(Utf8.COMPLETE, 0, numBytes)); - assertEquals(state1, - x.partialIsValidUtf8(Utf8.COMPLETE, 0, i)); - assertEquals(state1, - x.substring(0, i).partialIsValidUtf8(Utf8.COMPLETE, 0, i)); - assertEquals(state2, - x.partialIsValidUtf8(state1, i, j - i)); - assertEquals(state2, - x.substring(i, j).partialIsValidUtf8(state1, 0, j - i)); - assertEquals(state3, - x.partialIsValidUtf8(state2, j, numBytes - j)); - assertEquals(state3, - x.substring(j, numBytes) - .partialIsValidUtf8(state2, 0, numBytes - j)); + assertEquals(state1, x.partialIsValidUtf8(Utf8.COMPLETE, 0, i)); + assertEquals(state1, x.substring(0, i).partialIsValidUtf8(Utf8.COMPLETE, 0, i)); + assertEquals(state2, x.partialIsValidUtf8(state1, i, j - i)); + assertEquals(state2, x.substring(i, j).partialIsValidUtf8(state1, 0, j - i)); + assertEquals(state3, x.partialIsValidUtf8(state2, j, numBytes - j)); + assertEquals(state3, x.substring(j, numBytes).partialIsValidUtf8(state2, 0, numBytes - j)); } // ByteString reduplication should not affect its UTF-8 validity. - ByteString ropeADope = - RopeByteString.newInstanceForTest(bs, bs.substring(0, numBytes)); + ByteString ropeADope = RopeByteString.newInstanceForTest(bs, bs.substring(0, numBytes)); assertEquals(isRoundTrippable, ropeADope.isValidUtf8()); if (isRoundTrippable) { @@ -288,8 +319,7 @@ class IsValidUtf8TestUtil { } count++; if (byteChar != 0 && byteChar % 1000000L == 0) { - logger.info("Processed " + (byteChar / 1000000L) + - " million characters"); + logger.info("Processed " + (byteChar / 1000000L) + " million characters"); } } logger.info("Round tripped " + countRoundTripped + " of " + count); @@ -303,25 +333,26 @@ class IsValidUtf8TestUtil { * actual String class, it's possible for incompatibilities to develop * (although unlikely). * + * @param factory the factory for {@link ByteString} instances. * @param numBytes the number of bytes in the byte array * @param expectedCount the expected number of roundtrippable permutations * @param start the starting bytes encoded as a long as big-endian * @param lim the limit of bytes to process encoded as a long as big-endian, * or -1 to mean the max limit for numBytes */ - void testBytesUsingByteBuffers( - int numBytes, long expectedCount, long start, long lim) - throws UnsupportedEncodingException { - CharsetDecoder decoder = Internal.UTF_8.newDecoder() - .onMalformedInput(CodingErrorAction.REPLACE) - .onUnmappableCharacter(CodingErrorAction.REPLACE); - CharsetEncoder encoder = Internal.UTF_8.newEncoder() - .onMalformedInput(CodingErrorAction.REPLACE) - .onUnmappableCharacter(CodingErrorAction.REPLACE); + static void testBytesUsingByteBuffers( + ByteStringFactory factory, int numBytes, long expectedCount, long start, long lim) { + CharsetDecoder decoder = + Internal.UTF_8.newDecoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + CharsetEncoder encoder = + Internal.UTF_8.newEncoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); byte[] bytes = new byte[numBytes]; int maxChars = (int) (decoder.maxCharsPerByte() * numBytes) + 1; - char[] charsDecoded = - new char[(int) (decoder.maxCharsPerByte() * numBytes) + 1]; + char[] charsDecoded = new char[(int) (decoder.maxCharsPerByte() * numBytes) + 1]; int maxBytes = (int) (encoder.maxBytesPerChar() * maxChars) + 1; byte[] bytesReencoded = new byte[maxBytes]; @@ -347,7 +378,7 @@ class IsValidUtf8TestUtil { bytes[bytes.length - i - 1] = (byte) tmpByteChar; tmpByteChar = tmpByteChar >> 8; } - boolean isRoundTrippable = ByteString.copyFrom(bytes).isValidUtf8(); + boolean isRoundTrippable = factory.newByteString(bytes).isValidUtf8(); CoderResult result = decoder.decode(bb, cb, true); assertFalse(result.isError()); result = decoder.flush(cb); @@ -382,8 +413,7 @@ class IsValidUtf8TestUtil { countRoundTripped++; } if (byteChar != 0 && byteChar % 1000000 == 0) { - logger.info("Processed " + (byteChar / 1000000) + - " million characters"); + logger.info("Processed " + (byteChar / 1000000) + " million characters"); } } logger.info("Round tripped " + countRoundTripped + " of " + count); @@ -394,10 +424,9 @@ class IsValidUtf8TestUtil { outputFailure(byteChar, bytes, after, after.length); } - private static void outputFailure(long byteChar, byte[] bytes, byte[] after, - int len) { - fail("Failure: (" + Long.toHexString(byteChar) + ") " + - toHexString(bytes) + " => " + toHexString(after, len)); + private static void outputFailure(long byteChar, byte[] bytes, byte[] after, int len) { + fail("Failure: (" + Long.toHexString(byteChar) + ") " + toHexString(bytes) + " => " + + toHexString(after, len)); } private static String toHexString(byte[] b) { @@ -416,5 +445,4 @@ class IsValidUtf8TestUtil { s.append("\""); return s.toString(); } - } diff --git a/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java b/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java index 211b56977..8d1de6dc8 100644 --- a/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java +++ b/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java @@ -36,9 +36,10 @@ import static protobuf_unittest.UnittestProto.optionalInt64Extension; import protobuf_unittest.UnittestProto.TestAllExtensions; import protobuf_unittest.UnittestProto.TestAllTypes; -import java.io.IOException; import junit.framework.TestCase; +import java.io.IOException; + /** * Unit test for {@link LazyFieldLite}. * diff --git a/java/core/src/test/java/com/google/protobuf/LazyFieldTest.java b/java/core/src/test/java/com/google/protobuf/LazyFieldTest.java index 2b900065a..ce473ae9b 100644 --- a/java/core/src/test/java/com/google/protobuf/LazyFieldTest.java +++ b/java/core/src/test/java/com/google/protobuf/LazyFieldTest.java @@ -33,9 +33,10 @@ package com.google.protobuf; import protobuf_unittest.UnittestProto.TestAllExtensions; import protobuf_unittest.UnittestProto.TestAllTypes; -import java.io.IOException; import junit.framework.TestCase; +import java.io.IOException; + /** * Unit test for {@link LazyField}. * diff --git a/java/core/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java b/java/core/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java index 035917c83..29e8d8759 100644 --- a/java/core/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java +++ b/java/core/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java @@ -33,6 +33,8 @@ package com.google.protobuf; import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Bar; import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.BarPrime; import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Foo; +import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestOneofEquals; +import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestRecursiveOneof; import junit.framework.TestCase; @@ -83,6 +85,16 @@ public class LiteEqualsAndHashTest extends TestCase { assertFalse(bar.equals(barPrime)); } + public void testOneofEquals() throws Exception { + TestOneofEquals.Builder builder = TestOneofEquals.newBuilder(); + TestOneofEquals message1 = builder.build(); + // Set message2's name field to default value. The two messages should be different when we + // check with the oneof case. + builder.setName(""); + TestOneofEquals message2 = builder.build(); + assertFalse(message1.equals(message2)); + } + public void testEqualsAndHashCodeWithUnknownFields() throws InvalidProtocolBufferException { Foo fooWithOnlyValue = Foo.newBuilder() .setValue(1) @@ -105,4 +117,9 @@ public class LiteEqualsAndHashTest extends TestCase { assertFalse(o1.equals(o2)); assertFalse(o1.hashCode() == o2.hashCode()); } + + public void testRecursiveHashcode() { + // This tests that we don't infinite loop. + TestRecursiveOneof.getDefaultInstance().hashCode(); + } } diff --git a/java/core/src/test/java/com/google/protobuf/LiteTest.java b/java/core/src/test/java/com/google/protobuf/LiteTest.java index b1f298ff4..88c3e0b2a 100644 --- a/java/core/src/test/java/com/google/protobuf/LiteTest.java +++ b/java/core/src/test/java/com/google/protobuf/LiteTest.java @@ -33,22 +33,31 @@ package com.google.protobuf; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; +import com.google.protobuf.UnittestImportLite.ImportEnumLite; +import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite; import com.google.protobuf.UnittestLite; import com.google.protobuf.UnittestLite.ForeignEnumLite; import com.google.protobuf.UnittestLite.ForeignMessageLite; import com.google.protobuf.UnittestLite.TestAllExtensionsLite; import com.google.protobuf.UnittestLite.TestAllTypesLite; +import com.google.protobuf.UnittestLite.TestAllTypesLite.NestedEnum; import com.google.protobuf.UnittestLite.TestAllTypesLite.NestedMessage; import com.google.protobuf.UnittestLite.TestAllTypesLite.OneofFieldCase; import com.google.protobuf.UnittestLite.TestAllTypesLite.OptionalGroup; import com.google.protobuf.UnittestLite.TestAllTypesLite.RepeatedGroup; import com.google.protobuf.UnittestLite.TestAllTypesLiteOrBuilder; import com.google.protobuf.UnittestLite.TestNestedExtensionLite; +import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Bar; +import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.BarPrime; +import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Foo; +import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestOneofEquals; +import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestRecursiveOneof; import junit.framework.TestCase; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -58,6 +67,7 @@ import java.io.ObjectOutputStream; * @author kenton@google.com Kenton Varda */ public class LiteTest extends TestCase { + @Override public void setUp() throws Exception { // Test that nested extensions are initialized correctly even if the outer // class has not been accessed directly. This was once a bug with lite @@ -128,33 +138,7 @@ public class LiteTest extends TestCase { assertEquals(7, message2.getExtension( UnittestLite.optionalNestedMessageExtensionLite).getBb()); } - - public void testSerialize() throws Exception { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - TestAllTypesLite expected = - TestAllTypesLite.newBuilder() - .setOptionalInt32(123) - .addRepeatedString("hello") - .setOptionalNestedMessage( - TestAllTypesLite.NestedMessage.newBuilder().setBb(7)) - .build(); - ObjectOutputStream out = new ObjectOutputStream(baos); - try { - out.writeObject(expected); - } finally { - out.close(); - } - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - ObjectInputStream in = new ObjectInputStream(bais); - TestAllTypesLite actual = (TestAllTypesLite) in.readObject(); - assertEquals(expected.getOptionalInt32(), actual.getOptionalInt32()); - assertEquals(expected.getRepeatedStringCount(), - actual.getRepeatedStringCount()); - assertEquals(expected.getRepeatedString(0), - actual.getRepeatedString(0)); - assertEquals(expected.getOptionalNestedMessage().getBb(), - actual.getOptionalNestedMessage().getBb()); - } + public void testClone() { TestAllTypesLite.Builder expected = TestAllTypesLite.newBuilder() @@ -327,11 +311,9 @@ public class LiteTest extends TestCase { assertEquals( ForeignMessageLite.getDefaultInstance(), message.getOptionalForeignMessage()); - // LITE_RUNTIME doesn't implement equals so we compare on a property and - // ensure the property isn't set on foreignMessage. - assertEquals(3, builder.getOptionalForeignMessage().getC()); + assertEquals(foreignMessageBuilder.build(), builder.getOptionalForeignMessage()); messageAfterBuild = builder.build(); - assertEquals(3, messageAfterBuild.getOptionalForeignMessage().getC()); + assertEquals(foreignMessageBuilder.build(), messageAfterBuild.getOptionalForeignMessage()); assertEquals( ForeignMessageLite.getDefaultInstance(), message.getOptionalForeignMessage()); @@ -339,7 +321,7 @@ public class LiteTest extends TestCase { assertEquals( ForeignMessageLite.getDefaultInstance(), builder.getOptionalForeignMessage()); - assertEquals(3, messageAfterBuild.getOptionalForeignMessage().getC()); + assertEquals(foreignMessageBuilder.build(), messageAfterBuild.getOptionalForeignMessage()); message = builder.build(); OptionalGroup optionalGroup = OptionalGroup.newBuilder() @@ -364,17 +346,15 @@ public class LiteTest extends TestCase { builder.setOptionalGroup(optionalGroupBuilder); assertEquals( OptionalGroup.getDefaultInstance(), message.getOptionalGroup()); - // LITE_RUNTIME doesn't implement equals so we compare on a property and - // ensure the property isn't set on optionalGroup. - assertEquals(3, builder.getOptionalGroup().getA()); + assertEquals(optionalGroupBuilder.build(), builder.getOptionalGroup()); messageAfterBuild = builder.build(); - assertEquals(3, messageAfterBuild.getOptionalGroup().getA()); + assertEquals(optionalGroupBuilder.build(), messageAfterBuild.getOptionalGroup()); assertEquals( OptionalGroup.getDefaultInstance(), message.getOptionalGroup()); builder.clearOptionalGroup(); assertEquals( OptionalGroup.getDefaultInstance(), builder.getOptionalGroup()); - assertEquals(3, messageAfterBuild.getOptionalGroup().getA()); + assertEquals(optionalGroupBuilder.build(), messageAfterBuild.getOptionalGroup()); message = builder.build(); builder.setOptionalInt32(1); @@ -425,17 +405,16 @@ public class LiteTest extends TestCase { assertEquals( NestedMessage.getDefaultInstance(), message.getOptionalLazyMessage()); - // LITE_RUNTIME doesn't implement equals so we compare on a property. - assertEquals(3, builder.getOptionalLazyMessage().getBb()); + assertEquals(nestedMessageBuilder.build(), builder.getOptionalLazyMessage()); messageAfterBuild = builder.build(); - assertEquals(3, messageAfterBuild.getOptionalLazyMessage().getBb()); + assertEquals(nestedMessageBuilder.build(), messageAfterBuild.getOptionalLazyMessage()); assertEquals( NestedMessage.getDefaultInstance(), message.getOptionalLazyMessage()); builder.clearOptionalLazyMessage(); assertEquals( NestedMessage.getDefaultInstance(), builder.getOptionalLazyMessage()); - assertEquals(3, messageAfterBuild.getOptionalLazyMessage().getBb()); + assertEquals(nestedMessageBuilder.build(), messageAfterBuild.getOptionalLazyMessage()); message = builder.build(); builder.setOptionalSfixed32(1); @@ -1125,8 +1104,7 @@ public class LiteTest extends TestCase { assertEquals(0, message.getRepeatedForeignMessageCount()); builder.setRepeatedForeignMessage( 0, ForeignMessageLite.getDefaultInstance()); - // LITE_RUNTIME doesn't implement equals so we compare on a property. - assertEquals(3, messageAfterBuild.getRepeatedForeignMessage(0).getC()); + assertEquals(foreignMessageBuilder.build(), messageAfterBuild.getRepeatedForeignMessage(0)); assertEquals( ForeignMessageLite.getDefaultInstance(), builder.getRepeatedForeignMessage(0)); @@ -1139,8 +1117,7 @@ public class LiteTest extends TestCase { builder.setRepeatedForeignMessage(0, foreignMessageBuilder); assertEquals( foreignMessage, messageAfterBuild.getRepeatedForeignMessage(0)); - // LITE_RUNTIME doesn't implement equals so we compare on a property. - assertEquals(3, builder.getRepeatedForeignMessage(0).getC()); + assertEquals(foreignMessageBuilder.build(), builder.getRepeatedForeignMessage(0)); builder.clearRepeatedForeignMessage(); message = builder.build(); @@ -1173,9 +1150,7 @@ public class LiteTest extends TestCase { messageAfterBuild = builder.build(); assertEquals(0, message.getRepeatedGroupCount()); builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance()); - // LITE_RUNTIME doesn't implement equals so we compare on a property and - // ensure the property isn't set on repeatedGroup. - assertEquals(3, messageAfterBuild.getRepeatedGroup(0).getA()); + assertEquals(repeatedGroupBuilder.build(), messageAfterBuild.getRepeatedGroup(0)); assertEquals( RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0)); builder.clearRepeatedGroup(); @@ -1185,9 +1160,7 @@ public class LiteTest extends TestCase { messageAfterBuild = builder.build(); assertEquals(0, message.getRepeatedGroupCount()); builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance()); - // LITE_RUNTIME doesn't implement equals so we compare on a property and - // ensure the property isn't set on repeatedGroup. - assertEquals(3, messageAfterBuild.getRepeatedGroup(0).getA()); + assertEquals(repeatedGroupBuilder.build(), messageAfterBuild.getRepeatedGroup(0)); assertEquals( RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0)); builder.clearRepeatedGroup(); @@ -1235,9 +1208,7 @@ public class LiteTest extends TestCase { messageAfterBuild = builder.build(); assertEquals(0, message.getRepeatedLazyMessageCount()); builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance()); - // LITE_RUNTIME doesn't implement equals so we compare on a property and - // ensure the property isn't set on repeatedGroup. - assertEquals(3, messageAfterBuild.getRepeatedLazyMessage(0).getBb()); + assertEquals(nestedMessageBuilder.build(), messageAfterBuild.getRepeatedLazyMessage(0)); assertEquals( NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0)); builder.clearRepeatedLazyMessage(); @@ -1247,9 +1218,7 @@ public class LiteTest extends TestCase { messageAfterBuild = builder.build(); assertEquals(0, message.getRepeatedLazyMessageCount()); builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance()); - // LITE_RUNTIME doesn't implement equals so we compare on a property and - // ensure the property isn't set on repeatedGroup. - assertEquals(3, messageAfterBuild.getRepeatedLazyMessage(0).getBb()); + assertEquals(nestedMessageBuilder.build(), messageAfterBuild.getRepeatedLazyMessage(0)); assertEquals( NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0)); builder.clearRepeatedLazyMessage(); @@ -1459,4 +1428,802 @@ public class LiteTest extends TestCase { 11, (int) extendableMessage.getExtension( UnittestLite.optionalFixed32ExtensionLite)); } + + public void testToStringDefaultInstance() throws Exception { + assertToStringEquals("", TestAllTypesLite.getDefaultInstance()); + } + + public void testToStringPrimitives() throws Exception { + TestAllTypesLite proto = TestAllTypesLite.newBuilder() + .setOptionalInt32(1) + .setOptionalInt64(9223372036854775807L) + .build(); + assertToStringEquals("optional_int32: 1\noptional_int64: 9223372036854775807", proto); + + proto = TestAllTypesLite.newBuilder() + .setOptionalBool(true) + .setOptionalNestedEnum(TestAllTypesLite.NestedEnum.BAZ) + .build(); + assertToStringEquals("optional_bool: true\noptional_nested_enum: BAZ", proto); + + proto = TestAllTypesLite.newBuilder() + .setOptionalFloat(2.72f) + .setOptionalDouble(3.14) + .build(); + assertToStringEquals("optional_double: 3.14\noptional_float: 2.72", proto); + } + + public void testToStringStringFields() throws Exception { + TestAllTypesLite proto = TestAllTypesLite.newBuilder() + .setOptionalString("foo\"bar\nbaz\\") + .build(); + assertToStringEquals("optional_string: \"foo\\\"bar\\nbaz\\\\\"", proto); + + proto = TestAllTypesLite.newBuilder() + .setOptionalString("\u6587") + .build(); + assertToStringEquals("optional_string: \"\\346\\226\\207\"", proto); + } + + public void testToStringNestedMessage() throws Exception { + TestAllTypesLite proto = TestAllTypesLite.newBuilder() + .setOptionalNestedMessage(TestAllTypesLite.NestedMessage.getDefaultInstance()) + .build(); + assertToStringEquals("optional_nested_message {\n}", proto); + + proto = TestAllTypesLite.newBuilder() + .setOptionalNestedMessage( + TestAllTypesLite.NestedMessage.newBuilder().setBb(7)) + .build(); + assertToStringEquals("optional_nested_message {\n bb: 7\n}", proto); + } + + public void testToStringRepeatedFields() throws Exception { + TestAllTypesLite proto = TestAllTypesLite.newBuilder() + .addRepeatedInt32(32) + .addRepeatedInt32(32) + .addRepeatedInt64(64) + .build(); + assertToStringEquals("repeated_int32: 32\nrepeated_int32: 32\nrepeated_int64: 64", proto); + + proto = TestAllTypesLite.newBuilder() + .addRepeatedLazyMessage( + TestAllTypesLite.NestedMessage.newBuilder().setBb(7)) + .addRepeatedLazyMessage( + TestAllTypesLite.NestedMessage.newBuilder().setBb(8)) + .build(); + assertToStringEquals( + "repeated_lazy_message {\n bb: 7\n}\nrepeated_lazy_message {\n bb: 8\n}", + proto); + } + + public void testToStringForeignFields() throws Exception { + TestAllTypesLite proto = TestAllTypesLite.newBuilder() + .setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR) + .setOptionalForeignMessage( + ForeignMessageLite.newBuilder() + .setC(3)) + .build(); + assertToStringEquals( + "optional_foreign_enum: FOREIGN_LITE_BAR\noptional_foreign_message {\n c: 3\n}", + proto); + } + + public void testToStringExtensions() throws Exception { + TestAllExtensionsLite message = TestAllExtensionsLite.newBuilder() + .setExtension(UnittestLite.optionalInt32ExtensionLite, 123) + .addExtension(UnittestLite.repeatedStringExtensionLite, "spam") + .addExtension(UnittestLite.repeatedStringExtensionLite, "eggs") + .setExtension(UnittestLite.optionalNestedEnumExtensionLite, + TestAllTypesLite.NestedEnum.BAZ) + .setExtension(UnittestLite.optionalNestedMessageExtensionLite, + TestAllTypesLite.NestedMessage.newBuilder().setBb(7).build()) + .build(); + assertToStringEquals( + "[1]: 123\n[18] {\n bb: 7\n}\n[21]: 3\n[44]: \"spam\"\n[44]: \"eggs\"", + message); + } + + public void testToStringUnknownFields() throws Exception { + TestAllExtensionsLite messageWithExtensions = TestAllExtensionsLite.newBuilder() + .setExtension(UnittestLite.optionalInt32ExtensionLite, 123) + .addExtension(UnittestLite.repeatedStringExtensionLite, "spam") + .addExtension(UnittestLite.repeatedStringExtensionLite, "eggs") + .setExtension(UnittestLite.optionalNestedEnumExtensionLite, + TestAllTypesLite.NestedEnum.BAZ) + .setExtension(UnittestLite.optionalNestedMessageExtensionLite, + TestAllTypesLite.NestedMessage.newBuilder().setBb(7).build()) + .build(); + TestAllExtensionsLite messageWithUnknownFields = TestAllExtensionsLite.parseFrom( + messageWithExtensions.toByteArray()); + assertToStringEquals( + "1: 123\n18: \"\\b\\a\"\n21: 3\n44: \"spam\"\n44: \"eggs\"", + messageWithUnknownFields); + } + + public void testToStringLazyMessage() throws Exception { + TestAllTypesLite message = TestAllTypesLite.newBuilder() + .setOptionalLazyMessage(NestedMessage.newBuilder().setBb(1).build()) + .build(); + assertToStringEquals("optional_lazy_message {\n bb: 1\n}", message); + } + + public void testToStringGroup() throws Exception { + TestAllTypesLite message = TestAllTypesLite.newBuilder() + .setOptionalGroup(OptionalGroup.newBuilder().setA(1).build()) + .build(); + assertToStringEquals("optional_group {\n a: 1\n}", message); + } + + public void testToStringOneof() throws Exception { + TestAllTypesLite message = TestAllTypesLite.newBuilder() + .setOneofString("hello") + .build(); + assertToStringEquals("oneof_string: \"hello\"", message); + } + + // Asserts that the toString() representation of the message matches the expected. This verifies + // the first line starts with a comment; but, does not factor in said comment as part of the + // comparison as it contains unstable addresses. + private static void assertToStringEquals(String expected, MessageLite message) { + String toString = message.toString(); + assertEquals('#', toString.charAt(0)); + if (toString.indexOf("\n") >= 0) { + toString = toString.substring(toString.indexOf("\n") + 1); + } else { + toString = ""; + } + assertEquals(expected, toString); + } + + public void testParseLazy() throws Exception { + ByteString bb = TestAllTypesLite.newBuilder() + .setOptionalLazyMessage(NestedMessage.newBuilder() + .setBb(11) + .build()) + .build().toByteString(); + ByteString cc = TestAllTypesLite.newBuilder() + .setOptionalLazyMessage(NestedMessage.newBuilder() + .setCc(22) + .build()) + .build().toByteString(); + + ByteString concat = bb.concat(cc); + TestAllTypesLite message = TestAllTypesLite.parseFrom(concat); + + assertEquals(11, message.getOptionalLazyMessage().getBb()); + assertEquals(22L, message.getOptionalLazyMessage().getCc()); + } + + public void testParseLazy_oneOf() throws Exception { + ByteString bb = TestAllTypesLite.newBuilder() + .setOneofLazyNestedMessage(NestedMessage.newBuilder() + .setBb(11) + .build()) + .build().toByteString(); + ByteString cc = TestAllTypesLite.newBuilder() + .setOneofLazyNestedMessage(NestedMessage.newBuilder() + .setCc(22) + .build()) + .build().toByteString(); + + ByteString concat = bb.concat(cc); + TestAllTypesLite message = TestAllTypesLite.parseFrom(concat); + + assertEquals(11, message.getOneofLazyNestedMessage().getBb()); + assertEquals(22L, message.getOneofLazyNestedMessage().getCc()); + } + + public void testMergeFromStream_repeatedField() throws Exception { + TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder() + .addRepeatedString("hello"); + builder.mergeFrom(CodedInputStream.newInstance(builder.build().toByteArray())); + + assertEquals(2, builder.getRepeatedStringCount()); + } + + public void testMergeFromStream_invalidBytes() throws Exception { + TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder() + .setDefaultBool(true); + try { + builder.mergeFrom(CodedInputStream.newInstance("Invalid bytes".getBytes(Internal.UTF_8))); + fail(); + } catch (InvalidProtocolBufferException expected) {} + } + + public void testMergeFrom_sanity() throws Exception { + TestAllTypesLite one = TestUtilLite.getAllLiteSetBuilder().build(); + byte[] bytes = one.toByteArray(); + TestAllTypesLite two = TestAllTypesLite.parseFrom(bytes); + + one = one.toBuilder().mergeFrom(one).build(); + two = two.toBuilder().mergeFrom(bytes).build(); + assertEquals(one, two); + assertEquals(two, one); + assertEquals(one.hashCode(), two.hashCode()); + } + + public void testEquals_notEqual() throws Exception { + TestAllTypesLite one = TestUtilLite.getAllLiteSetBuilder().build(); + byte[] bytes = one.toByteArray(); + TestAllTypesLite two = one.toBuilder().mergeFrom(one).mergeFrom(bytes).build(); + + assertFalse(one.equals(two)); + assertFalse(two.equals(one)); + + assertFalse(one.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(one)); + + TestAllTypesLite oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultBool(true) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultCord("") + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultCordBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultDouble(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultFixed32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultFixed64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultFloat(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultImportEnum(ImportEnumLite.IMPORT_LITE_BAR) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultInt32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultInt64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultNestedEnum(NestedEnum.BAR) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultSfixed32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultSfixed64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultSint32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultSint64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultString("") + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultStringBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultStringPiece("") + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultStringPieceBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultUint32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setDefaultUint64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedBool(true) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedCord("") + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedCordBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedDouble(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedFixed32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedFixed64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedFloat(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedImportEnum(ImportEnumLite.IMPORT_LITE_BAR) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedInt32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedInt64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedNestedEnum(NestedEnum.BAR) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedSfixed32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedSfixed64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedSint32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedSint64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedString("") + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedStringBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedStringPiece("") + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedStringPieceBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedUint32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedUint64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalBool(true) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalCord("") + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalCordBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalDouble(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalFixed32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalFixed64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalFloat(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalImportEnum(ImportEnumLite.IMPORT_LITE_BAR) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalInt32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalInt64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalNestedEnum(NestedEnum.BAR) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalSfixed32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalSfixed64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalSint32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalSint64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalString("") + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalStringBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalStringPiece("") + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalStringPieceBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalUint32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalUint64(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOneofBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOneofLazyNestedMessage(NestedMessage.getDefaultInstance()) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOneofNestedMessage(NestedMessage.getDefaultInstance()) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOneofString("") + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOneofStringBytes(ByteString.EMPTY) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOneofUint32(0) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalForeignMessage(ForeignMessageLite.getDefaultInstance()) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalGroup(OptionalGroup.getDefaultInstance()) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalPublicImportMessage(PublicImportMessageLite.getDefaultInstance()) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + + oneFieldSet = TestAllTypesLite.newBuilder() + .setOptionalLazyMessage(NestedMessage.getDefaultInstance()) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + oneFieldSet = TestAllTypesLite.newBuilder() + .addRepeatedLazyMessage(NestedMessage.getDefaultInstance()) + .build(); + assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance())); + assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet)); + } + + public void testEquals() throws Exception { + // Check that two identical objs are equal. + Foo foo1a = Foo.newBuilder() + .setValue(1) + .addBar(Bar.newBuilder().setName("foo1")) + .build(); + Foo foo1b = Foo.newBuilder() + .setValue(1) + .addBar(Bar.newBuilder().setName("foo1")) + .build(); + Foo foo2 = Foo.newBuilder() + .setValue(1) + .addBar(Bar.newBuilder().setName("foo2")) + .build(); + + // Check that equals is doing value rather than object equality. + assertEquals(foo1a, foo1b); + assertEquals(foo1a.hashCode(), foo1b.hashCode()); + + // Check that a diffeent object is not equal. + assertFalse(foo1a.equals(foo2)); + + // Check that two objects which have different types but the same field values are not + // considered to be equal. + Bar bar = Bar.newBuilder().setName("bar").build(); + BarPrime barPrime = BarPrime.newBuilder().setName("bar").build(); + assertFalse(bar.equals(barPrime)); + } + + public void testOneofEquals() throws Exception { + TestOneofEquals.Builder builder = TestOneofEquals.newBuilder(); + TestOneofEquals message1 = builder.build(); + // Set message2's name field to default value. The two messages should be different when we + // check with the oneof case. + builder.setName(""); + TestOneofEquals message2 = builder.build(); + assertFalse(message1.equals(message2)); + } + + public void testEquals_sanity() throws Exception { + TestAllTypesLite one = TestUtilLite.getAllLiteSetBuilder().build(); + TestAllTypesLite two = TestAllTypesLite.parseFrom(one.toByteArray()); + assertEquals(one, two); + assertEquals(one.hashCode(), two.hashCode()); + + assertEquals( + one.toBuilder().mergeFrom(two).build(), + two.toBuilder().mergeFrom(two.toByteArray()).build()); + } + + public void testEqualsAndHashCodeWithUnknownFields() throws InvalidProtocolBufferException { + Foo fooWithOnlyValue = Foo.newBuilder() + .setValue(1) + .build(); + + Foo fooWithValueAndExtension = fooWithOnlyValue.toBuilder() + .setValue(1) + .setExtension(Bar.fooExt, Bar.newBuilder() + .setName("name") + .build()) + .build(); + + Foo fooWithValueAndUnknownFields = Foo.parseFrom(fooWithValueAndExtension.toByteArray()); + + assertEqualsAndHashCodeAreFalse(fooWithOnlyValue, fooWithValueAndUnknownFields); + assertEqualsAndHashCodeAreFalse(fooWithValueAndExtension, fooWithValueAndUnknownFields); + } + + // Test to ensure we avoid a class cast exception with oneofs. + public void testEquals_oneOfMessages() { + TestAllTypesLite mine = TestAllTypesLite.newBuilder() + .setOneofString("Hello") + .build(); + + TestAllTypesLite other = TestAllTypesLite.newBuilder() + .setOneofNestedMessage(NestedMessage.getDefaultInstance()) + .build(); + + assertFalse(mine.equals(other)); + assertFalse(other.equals(mine)); + } + + private void assertEqualsAndHashCodeAreFalse(Object o1, Object o2) { + assertFalse(o1.equals(o2)); + assertFalse(o1.hashCode() == o2.hashCode()); + } + + public void testRecursiveHashcode() { + // This tests that we don't infinite loop. + TestRecursiveOneof.getDefaultInstance().hashCode(); + } } diff --git a/java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java b/java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java index 68b55ceb9..2e7792a88 100644 --- a/java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java +++ b/java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java @@ -47,9 +47,9 @@ import java.util.List; import java.util.NoSuchElementException; /** - * Test {@link LiteralByteString} by setting up a reference string in {@link #setUp()}. - * This class is designed to be extended for testing extensions of {@link LiteralByteString} - * such as {@link BoundedByteString}, see {@link BoundedByteStringTest}. + * Test {@code LiteralByteString} by setting up a reference string in {@link #setUp()}. + * This class is designed to be extended for testing extensions of {@code LiteralByteString} + * such as {@code BoundedByteString}, see {@link BoundedByteStringTest}. * * @author carlanton@google.com (Carl Haverl) */ @@ -304,25 +304,75 @@ public class LiteralByteStringTest extends TestCase { Arrays.equals(referenceBytes, roundTripBytes)); } - public void testWriteTo_mutating() throws IOException { + public void testWriteToShouldNotExposeInternalBufferToOutputStream() throws IOException { OutputStream os = new OutputStream() { @Override public void write(byte[] b, int off, int len) { - for (int x = 0; x < len; ++x) { - b[off + x] = (byte) 0; - } + Arrays.fill(b, off, off + len, (byte) 0); } @Override public void write(int b) { - // Purposefully left blank. + throw new UnsupportedOperationException(); } }; stringUnderTest.writeTo(os); - byte[] newBytes = stringUnderTest.toByteArray(); assertTrue(classUnderTest + ".writeTo() must not grant access to underlying array", - Arrays.equals(referenceBytes, newBytes)); + Arrays.equals(referenceBytes, stringUnderTest.toByteArray())); + } + + public void testWriteToInternalShouldExposeInternalBufferToOutputStream() throws IOException { + OutputStream os = new OutputStream() { + @Override + public void write(byte[] b, int off, int len) { + Arrays.fill(b, off, off + len, (byte) 0); + } + + @Override + public void write(int b) { + throw new UnsupportedOperationException(); + } + }; + + stringUnderTest.writeToInternal(os, 0, stringUnderTest.size()); + byte[] allZeros = new byte[stringUnderTest.size()]; + assertTrue(classUnderTest + ".writeToInternal() must grant access to underlying array", + Arrays.equals(allZeros, stringUnderTest.toByteArray())); + } + + public void testWriteToShouldExposeInternalBufferToByteOutput() throws IOException { + ByteOutput out = new ByteOutput() { + @Override + public void write(byte value) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void write(byte[] value, int offset, int length) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void writeLazy(byte[] value, int offset, int length) throws IOException { + Arrays.fill(value, offset, offset + length, (byte) 0); + } + + @Override + public void write(ByteBuffer value) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void writeLazy(ByteBuffer value) throws IOException { + throw new UnsupportedOperationException(); + } + }; + + stringUnderTest.writeTo(out); + byte[] allZeros = new byte[stringUnderTest.size()]; + assertTrue(classUnderTest + ".writeToInternal() must grant access to underlying array", + Arrays.equals(allZeros, stringUnderTest.toByteArray())); } public void testNewOutput() throws IOException { diff --git a/java/core/src/test/java/com/google/protobuf/LongArrayListTest.java b/java/core/src/test/java/com/google/protobuf/LongArrayListTest.java index 1bd094f70..0a8f9ed22 100644 --- a/java/core/src/test/java/com/google/protobuf/LongArrayListTest.java +++ b/java/core/src/test/java/com/google/protobuf/LongArrayListTest.java @@ -73,20 +73,6 @@ public class LongArrayListTest extends TestCase { assertImmutable(list); } - public void testCopyConstructor() { - LongArrayList copy = new LongArrayList(TERTIARY_LIST); - assertEquals(TERTIARY_LIST, copy); - - copy = new LongArrayList(LongArrayList.emptyList()); - assertEquals(LongArrayList.emptyList(), copy); - - copy = new LongArrayList(asList(1L, 2L, 3L)); - assertEquals(asList(1L, 2L, 3L), copy); - - copy = new LongArrayList(Collections.emptyList()); - assertEquals(LongArrayList.emptyList(), copy); - } - public void testModificationWithIteration() { list.addAll(asList(1L, 2L, 3L, 4L)); Iterator iterator = list.iterator(); diff --git a/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java b/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java index 3d8c9bc4b..d79d00299 100644 --- a/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java +++ b/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java @@ -432,7 +432,7 @@ public class MapForProto2LiteTest extends TestCase { assertEquals(1, messageWithUnknownEnums.getInt32ToInt32Field().get(1).intValue()); assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue()); } - + public void testIterationOrder() throws Exception { TestMap.Builder builder = TestMap.newBuilder(); diff --git a/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java b/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java index 1fa3cbdba..73154c0fc 100644 --- a/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java +++ b/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java @@ -36,7 +36,6 @@ import map_test.MapForProto2TestProto.TestMap.MessageValue; import map_test.MapForProto2TestProto.TestMap.MessageWithRequiredFields; import map_test.MapForProto2TestProto.TestRecursiveMap; import map_test.MapForProto2TestProto.TestUnknownEnumValue; - import junit.framework.TestCase; import java.util.ArrayList; diff --git a/java/core/src/test/java/com/google/protobuf/MapTest.java b/java/core/src/test/java/com/google/protobuf/MapTest.java index 0e5c12840..1dc5787d9 100644 --- a/java/core/src/test/java/com/google/protobuf/MapTest.java +++ b/java/core/src/test/java/com/google/protobuf/MapTest.java @@ -37,7 +37,6 @@ import com.google.protobuf.Descriptors.FieldDescriptor; import map_test.MapTestProto.TestMap; import map_test.MapTestProto.TestMap.MessageValue; import map_test.MapTestProto.TestOnChangeEventPropagation; - import junit.framework.TestCase; import java.util.ArrayList; diff --git a/java/core/src/test/java/com/google/protobuf/MessageTest.java b/java/core/src/test/java/com/google/protobuf/MessageTest.java index abcd3a1db..dcd1aba78 100644 --- a/java/core/src/test/java/com/google/protobuf/MessageTest.java +++ b/java/core/src/test/java/com/google/protobuf/MessageTest.java @@ -30,11 +30,11 @@ package com.google.protobuf; -import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.ForeignMessage; import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestAllTypes; import protobuf_unittest.UnittestProto.TestRequired; import protobuf_unittest.UnittestProto.TestRequiredForeign; -import protobuf_unittest.UnittestProto.ForeignMessage; import junit.framework.TestCase; diff --git a/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java b/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java index 236531265..542e28c0a 100644 --- a/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java +++ b/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java @@ -35,8 +35,8 @@ import protobuf_unittest.Wheel; import junit.framework.TestCase; -import java.util.List; import java.util.ArrayList; +import java.util.List; /** * Test cases that exercise end-to-end use cases involving diff --git a/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java b/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java index e40a36626..6be5b93c3 100644 --- a/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java +++ b/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java @@ -41,6 +41,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; @@ -56,11 +57,12 @@ public class NioByteStringTest extends TestCase { private static final String CLASSNAME = NioByteString.class.getSimpleName(); private static final byte[] BYTES = ByteStringTest.getTestBytes(1234, 11337766L); private static final int EXPECTED_HASH = ByteString.wrap(BYTES).hashCode(); - private static final ByteBuffer BUFFER = ByteBuffer.wrap(BYTES.clone()); - private static final ByteString TEST_STRING = new NioByteString(BUFFER); + + private final ByteBuffer backingBuffer = ByteBuffer.wrap(BYTES.clone()); + private final ByteString testString = new NioByteString(backingBuffer); public void testExpectedType() { - String actualClassName = getActualClassName(TEST_STRING); + String actualClassName = getActualClassName(testString); assertEquals(CLASSNAME + " should match type exactly", CLASSNAME, actualClassName); } @@ -73,14 +75,14 @@ public class NioByteStringTest extends TestCase { public void testByteAt() { boolean stillEqual = true; for (int i = 0; stillEqual && i < BYTES.length; ++i) { - stillEqual = (BYTES[i] == TEST_STRING.byteAt(i)); + stillEqual = (BYTES[i] == testString.byteAt(i)); } assertTrue(CLASSNAME + " must capture the right bytes", stillEqual); } public void testByteIterator() { boolean stillEqual = true; - ByteString.ByteIterator iter = TEST_STRING.iterator(); + ByteString.ByteIterator iter = testString.iterator(); for (int i = 0; stillEqual && i < BYTES.length; ++i) { stillEqual = (iter.hasNext() && BYTES[i] == iter.nextByte()); } @@ -98,7 +100,7 @@ public class NioByteStringTest extends TestCase { public void testByteIterable() { boolean stillEqual = true; int j = 0; - for (byte quantum : TEST_STRING) { + for (byte quantum : testString) { stillEqual = (BYTES[j] == quantum); ++j; } @@ -108,15 +110,15 @@ public class NioByteStringTest extends TestCase { public void testSize() { assertEquals(CLASSNAME + " must have the expected size", BYTES.length, - TEST_STRING.size()); + testString.size()); } public void testGetTreeDepth() { - assertEquals(CLASSNAME + " must have depth 0", 0, TEST_STRING.getTreeDepth()); + assertEquals(CLASSNAME + " must have depth 0", 0, testString.getTreeDepth()); } public void testIsBalanced() { - assertTrue(CLASSNAME + " is technically balanced", TEST_STRING.isBalanced()); + assertTrue(CLASSNAME + " is technically balanced", testString.isBalanced()); } public void testCopyTo_ByteArrayOffsetLength() { @@ -124,7 +126,7 @@ public class NioByteStringTest extends TestCase { int length = 100; byte[] destination = new byte[destinationOffset + length]; int sourceOffset = 213; - TEST_STRING.copyTo(destination, sourceOffset, destinationOffset, length); + testString.copyTo(destination, sourceOffset, destinationOffset, length); boolean stillEqual = true; for (int i = 0; stillEqual && i < length; ++i) { stillEqual = BYTES[i + sourceOffset] == destination[i + destinationOffset]; @@ -139,7 +141,7 @@ public class NioByteStringTest extends TestCase { try { // Copy one too many bytes - TEST_STRING.copyTo(destination, TEST_STRING.size() + 1 - length, + testString.copyTo(destination, testString.size() + 1 - length, destinationOffset, length); fail("Should have thrown an exception when copying too many bytes of a " + CLASSNAME); @@ -149,7 +151,7 @@ public class NioByteStringTest extends TestCase { try { // Copy with illegal negative sourceOffset - TEST_STRING.copyTo(destination, -1, destinationOffset, length); + testString.copyTo(destination, -1, destinationOffset, length); fail("Should have thrown an exception when given a negative sourceOffset in " + CLASSNAME); } catch (IndexOutOfBoundsException expected) { @@ -158,7 +160,7 @@ public class NioByteStringTest extends TestCase { try { // Copy with illegal negative destinationOffset - TEST_STRING.copyTo(destination, 0, -1, length); + testString.copyTo(destination, 0, -1, length); fail("Should have thrown an exception when given a negative destinationOffset in " + CLASSNAME); } catch (IndexOutOfBoundsException expected) { @@ -167,7 +169,7 @@ public class NioByteStringTest extends TestCase { try { // Copy with illegal negative size - TEST_STRING.copyTo(destination, 0, 0, -1); + testString.copyTo(destination, 0, 0, -1); fail("Should have thrown an exception when given a negative size in " + CLASSNAME); } catch (IndexOutOfBoundsException expected) { @@ -176,7 +178,7 @@ public class NioByteStringTest extends TestCase { try { // Copy with illegal too-large sourceOffset - TEST_STRING.copyTo(destination, 2 * TEST_STRING.size(), 0, length); + testString.copyTo(destination, 2 * testString.size(), 0, length); fail("Should have thrown an exception when the destinationOffset is too large in " + CLASSNAME); } catch (IndexOutOfBoundsException expected) { @@ -185,7 +187,7 @@ public class NioByteStringTest extends TestCase { try { // Copy with illegal too-large destinationOffset - TEST_STRING.copyTo(destination, 0, 2 * destination.length, length); + testString.copyTo(destination, 0, 2 * destination.length, length); fail("Should have thrown an exception when the destinationOffset is too large in " + CLASSNAME); } catch (IndexOutOfBoundsException expected) { @@ -196,21 +198,21 @@ public class NioByteStringTest extends TestCase { public void testCopyTo_ByteBuffer() { // Same length. ByteBuffer myBuffer = ByteBuffer.allocate(BYTES.length); - TEST_STRING.copyTo(myBuffer); + testString.copyTo(myBuffer); myBuffer.flip(); assertEquals(CLASSNAME + ".copyTo(ByteBuffer) must give back the same bytes", - BUFFER, myBuffer); + backingBuffer, myBuffer); // Target buffer bigger than required. - myBuffer = ByteBuffer.allocate(TEST_STRING.size() + 1); - TEST_STRING.copyTo(myBuffer); + myBuffer = ByteBuffer.allocate(testString.size() + 1); + testString.copyTo(myBuffer); myBuffer.flip(); - assertEquals(BUFFER, myBuffer); + assertEquals(backingBuffer, myBuffer); // Target buffer has no space. myBuffer = ByteBuffer.allocate(0); try { - TEST_STRING.copyTo(myBuffer); + testString.copyTo(myBuffer); fail("Should have thrown an exception when target ByteBuffer has insufficient capacity"); } catch (BufferOverflowException e) { // Expected. @@ -219,7 +221,7 @@ public class NioByteStringTest extends TestCase { // Target buffer too small. myBuffer = ByteBuffer.allocate(1); try { - TEST_STRING.copyTo(myBuffer); + testString.copyTo(myBuffer); fail("Should have thrown an exception when target ByteBuffer has insufficient capacity"); } catch (BufferOverflowException e) { // Expected. @@ -227,26 +229,26 @@ public class NioByteStringTest extends TestCase { } public void testMarkSupported() { - InputStream stream = TEST_STRING.newInput(); + InputStream stream = testString.newInput(); assertTrue(CLASSNAME + ".newInput() must support marking", stream.markSupported()); } public void testMarkAndReset() throws IOException { - int fraction = TEST_STRING.size() / 3; + int fraction = testString.size() / 3; - InputStream stream = TEST_STRING.newInput(); - stream.mark(TEST_STRING.size()); // First, mark() the end. + InputStream stream = testString.newInput(); + stream.mark(testString.size()); // First, mark() the end. skipFully(stream, fraction); // Skip a large fraction, but not all. assertEquals( CLASSNAME + ": after skipping to the 'middle', half the bytes are available", - (TEST_STRING.size() - fraction), stream.available()); + (testString.size() - fraction), stream.available()); stream.reset(); assertEquals( CLASSNAME + ": after resetting, all bytes are available", - TEST_STRING.size(), stream.available()); + testString.size(), stream.available()); - skipFully(stream, TEST_STRING.size()); // Skip to the end. + skipFully(stream, testString.size()); // Skip to the end. assertEquals( CLASSNAME + ": after skipping to the end, no more bytes are available", 0, stream.available()); @@ -284,7 +286,7 @@ public class NioByteStringTest extends TestCase { } public void testAsReadOnlyByteBuffer() { - ByteBuffer byteBuffer = TEST_STRING.asReadOnlyByteBuffer(); + ByteBuffer byteBuffer = testString.asReadOnlyByteBuffer(); byte[] roundTripBytes = new byte[BYTES.length]; assertTrue(byteBuffer.remaining() == BYTES.length); assertTrue(byteBuffer.isReadOnly()); @@ -294,7 +296,7 @@ public class NioByteStringTest extends TestCase { } public void testAsReadOnlyByteBufferList() { - List byteBuffers = TEST_STRING.asReadOnlyByteBufferList(); + List byteBuffers = testString.asReadOnlyByteBufferList(); int bytesSeen = 0; byte[] roundTripBytes = new byte[BYTES.length]; for (ByteBuffer byteBuffer : byteBuffers) { @@ -310,25 +312,98 @@ public class NioByteStringTest extends TestCase { } public void testToByteArray() { - byte[] roundTripBytes = TEST_STRING.toByteArray(); + byte[] roundTripBytes = testString.toByteArray(); assertTrue(CLASSNAME + ".toByteArray() must give back the same bytes", Arrays.equals(BYTES, roundTripBytes)); } public void testWriteTo() throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); - TEST_STRING.writeTo(bos); + testString.writeTo(bos); byte[] roundTripBytes = bos.toByteArray(); assertTrue(CLASSNAME + ".writeTo() must give back the same bytes", Arrays.equals(BYTES, roundTripBytes)); } + public void testWriteToShouldNotExposeInternalBufferToOutputStream() throws IOException { + OutputStream os = new OutputStream() { + @Override + public void write(byte[] b, int off, int len) { + Arrays.fill(b, off, off + len, (byte) 0); + } + + @Override + public void write(int b) { + throw new UnsupportedOperationException(); + } + }; + + byte[] original = Arrays.copyOf(BYTES, BYTES.length); + testString.writeTo(os); + assertTrue(CLASSNAME + ".writeTo() must NOT grant access to underlying buffer", + Arrays.equals(original, BYTES)); + } + + public void testWriteToInternalShouldExposeInternalBufferToOutputStream() throws IOException { + OutputStream os = new OutputStream() { + @Override + public void write(byte[] b, int off, int len) { + Arrays.fill(b, off, off + len, (byte) 0); + } + + @Override + public void write(int b) { + throw new UnsupportedOperationException(); + } + }; + + testString.writeToInternal(os, 0, testString.size()); + byte[] allZeros = new byte[testString.size()]; + assertTrue(CLASSNAME + ".writeToInternal() must grant access to underlying buffer", + Arrays.equals(allZeros, backingBuffer.array())); + } + + public void testWriteToShouldExposeInternalBufferToByteOutput() throws IOException { + ByteOutput out = new ByteOutput() { + @Override + public void write(byte value) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void write(byte[] value, int offset, int length) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void writeLazy(byte[] value, int offset, int length) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void write(ByteBuffer value) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void writeLazy(ByteBuffer value) throws IOException { + Arrays.fill(value.array(), value.arrayOffset(), value.arrayOffset() + value.limit(), + (byte) 0); + } + }; + + testString.writeTo(out); + byte[] allZeros = new byte[testString.size()]; + assertTrue(CLASSNAME + ".writeTo() must grant access to underlying buffer", + Arrays.equals(allZeros, backingBuffer.array())); + } + public void testNewOutput() throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteString.Output output = ByteString.newOutput(); - TEST_STRING.writeTo(output); + testString.writeTo(output); assertEquals("Output Size returns correct result", - output.size(), TEST_STRING.size()); + output.size(), testString.size()); output.writeTo(bos); assertTrue("Output.writeTo() must give back the same bytes", Arrays.equals(BYTES, bos.toByteArray())); @@ -336,7 +411,7 @@ public class NioByteStringTest extends TestCase { // write the output stream to itself! This should cause it to double output.writeTo(output); assertEquals("Writing an output stream to itself is successful", - TEST_STRING.concat(TEST_STRING), output.toByteString()); + testString.concat(testString), output.toByteString()); output.reset(); assertEquals("Output.reset() resets the output", 0, output.size()); @@ -373,7 +448,7 @@ public class NioByteStringTest extends TestCase { } try { - TEST_STRING.toString("invalid"); + testString.toString("invalid"); fail("Should have thrown an exception."); } catch (UnsupportedEncodingException expected) { // This is success @@ -381,36 +456,36 @@ public class NioByteStringTest extends TestCase { } public void testEquals() { - assertEquals(CLASSNAME + " must not equal null", false, TEST_STRING.equals(null)); - assertEquals(CLASSNAME + " must equal self", TEST_STRING, TEST_STRING); + assertEquals(CLASSNAME + " must not equal null", false, testString.equals(null)); + assertEquals(CLASSNAME + " must equal self", testString, testString); assertFalse(CLASSNAME + " must not equal the empty string", - TEST_STRING.equals(EMPTY)); + testString.equals(EMPTY)); assertEquals(CLASSNAME + " empty strings must be equal", - EMPTY, TEST_STRING.substring(55, 55)); + EMPTY, testString.substring(55, 55)); assertEquals(CLASSNAME + " must equal another string with the same value", - TEST_STRING, new NioByteString(BUFFER)); + testString, new NioByteString(backingBuffer)); byte[] mungedBytes = mungedBytes(); assertFalse(CLASSNAME + " must not equal every string with the same length", - TEST_STRING.equals(new NioByteString(ByteBuffer.wrap(mungedBytes)))); + testString.equals(new NioByteString(ByteBuffer.wrap(mungedBytes)))); } public void testEqualsLiteralByteString() { ByteString literal = ByteString.copyFrom(BYTES); assertEquals(CLASSNAME + " must equal LiteralByteString with same value", literal, - TEST_STRING); - assertEquals(CLASSNAME + " must equal LiteralByteString with same value", TEST_STRING, + testString); + assertEquals(CLASSNAME + " must equal LiteralByteString with same value", testString, literal); assertFalse(CLASSNAME + " must not equal the empty string", - TEST_STRING.equals(ByteString.EMPTY)); + testString.equals(ByteString.EMPTY)); assertEquals(CLASSNAME + " empty strings must be equal", - ByteString.EMPTY, TEST_STRING.substring(55, 55)); + ByteString.EMPTY, testString.substring(55, 55)); literal = ByteString.copyFrom(mungedBytes()); assertFalse(CLASSNAME + " must not equal every LiteralByteString with the same length", - TEST_STRING.equals(literal)); + testString.equals(literal)); assertFalse(CLASSNAME + " must not equal every LiteralByteString with the same length", - literal.equals(TEST_STRING)); + literal.equals(testString)); } public void testEqualsRopeByteString() { @@ -419,22 +494,22 @@ public class NioByteStringTest extends TestCase { ByteString rope = p1.concat(p2); assertEquals(CLASSNAME + " must equal RopeByteString with same value", rope, - TEST_STRING); - assertEquals(CLASSNAME + " must equal RopeByteString with same value", TEST_STRING, + testString); + assertEquals(CLASSNAME + " must equal RopeByteString with same value", testString, rope); assertFalse(CLASSNAME + " must not equal the empty string", - TEST_STRING.equals(ByteString.EMPTY.concat(ByteString.EMPTY))); + testString.equals(ByteString.EMPTY.concat(ByteString.EMPTY))); assertEquals(CLASSNAME + " empty strings must be equal", - ByteString.EMPTY.concat(ByteString.EMPTY), TEST_STRING.substring(55, 55)); + ByteString.EMPTY.concat(ByteString.EMPTY), testString.substring(55, 55)); byte[] mungedBytes = mungedBytes(); p1 = ByteString.copyFrom(mungedBytes, 0, 5); p2 = ByteString.copyFrom(mungedBytes, 5, mungedBytes.length - 5); rope = p1.concat(p2); assertFalse(CLASSNAME + " must not equal every RopeByteString with the same length", - TEST_STRING.equals(rope)); + testString.equals(rope)); assertFalse(CLASSNAME + " must not equal every RopeByteString with the same length", - rope.equals(TEST_STRING)); + rope.equals(testString)); } private byte[] mungedBytes() { @@ -445,12 +520,12 @@ public class NioByteStringTest extends TestCase { } public void testHashCode() { - int hash = TEST_STRING.hashCode(); + int hash = testString.hashCode(); assertEquals(CLASSNAME + " must have expected hashCode", EXPECTED_HASH, hash); } public void testPeekCachedHashCode() { - ByteString newString = new NioByteString(BUFFER); + ByteString newString = new NioByteString(backingBuffer); assertEquals(CLASSNAME + ".peekCachedHashCode() should return zero at first", 0, newString.peekCachedHashCode()); newString.hashCode(); @@ -461,15 +536,15 @@ public class NioByteStringTest extends TestCase { public void testPartialHash() { // partialHash() is more strenuously tested elsewhere by testing hashes of substrings. // This test would fail if the expected hash were 1. It's not. - int hash = TEST_STRING.partialHash(TEST_STRING.size(), 0, TEST_STRING.size()); + int hash = testString.partialHash(testString.size(), 0, testString.size()); assertEquals(CLASSNAME + ".partialHash() must yield expected hashCode", EXPECTED_HASH, hash); } public void testNewInput() throws IOException { - InputStream input = TEST_STRING.newInput(); + InputStream input = testString.newInput(); assertEquals("InputStream.available() returns correct value", - TEST_STRING.size(), input.available()); + testString.size(), input.available()); boolean stillEqual = true; for (byte referenceByte : BYTES) { int expectedInt = (referenceByte & 0xFF); @@ -482,8 +557,8 @@ public class NioByteStringTest extends TestCase { } public void testNewInput_skip() throws IOException { - InputStream input = TEST_STRING.newInput(); - int stringSize = TEST_STRING.size(); + InputStream input = testString.newInput(); + int stringSize = testString.size(); int nearEndIndex = stringSize * 2 / 3; long skipped1 = input.skip(nearEndIndex); assertEquals("InputStream.skip()", skipped1, nearEndIndex); @@ -492,7 +567,7 @@ public class NioByteStringTest extends TestCase { assertTrue("InputStream.mark() is available", input.markSupported()); input.mark(0); assertEquals("InputStream.skip(), read()", - TEST_STRING.byteAt(nearEndIndex) & 0xFF, input.read()); + testString.byteAt(nearEndIndex) & 0xFF, input.read()); assertEquals("InputStream.available()", stringSize - skipped1 - 1, input.available()); long skipped2 = input.skip(stringSize); @@ -504,11 +579,11 @@ public class NioByteStringTest extends TestCase { assertEquals("InputStream.reset() succeded", stringSize - skipped1, input.available()); assertEquals("InputStream.reset(), read()", - TEST_STRING.byteAt(nearEndIndex) & 0xFF, input.read()); + testString.byteAt(nearEndIndex) & 0xFF, input.read()); } public void testNewCodedInput() throws IOException { - CodedInputStream cis = TEST_STRING.newCodedInput(); + CodedInputStream cis = testString.newCodedInput(); byte[] roundTripBytes = cis.readRawBytes(BYTES.length); assertTrue(CLASSNAME + " must give the same bytes back from the CodedInputStream", Arrays.equals(BYTES, roundTripBytes)); @@ -521,22 +596,22 @@ public class NioByteStringTest extends TestCase { */ public void testConcat_empty() { assertSame(CLASSNAME + " concatenated with empty must give " + CLASSNAME, - TEST_STRING.concat(EMPTY), TEST_STRING); + testString.concat(EMPTY), testString); assertSame("empty concatenated with " + CLASSNAME + " must give " + CLASSNAME, - EMPTY.concat(TEST_STRING), TEST_STRING); + EMPTY.concat(testString), testString); } public void testJavaSerialization() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(out); - oos.writeObject(TEST_STRING); + oos.writeObject(testString); oos.close(); byte[] pickled = out.toByteArray(); InputStream in = new ByteArrayInputStream(pickled); ObjectInputStream ois = new ObjectInputStream(in); Object o = ois.readObject(); assertTrue("Didn't get a ByteString back", o instanceof ByteString); - assertEquals("Should get an equal ByteString back", TEST_STRING, o); + assertEquals("Should get an equal ByteString back", testString, o); } private static ByteString forString(String str) { diff --git a/java/core/src/test/java/com/google/protobuf/ParseExceptionsTest.java b/java/core/src/test/java/com/google/protobuf/ParseExceptionsTest.java index 37fa242d8..bf1f1d71f 100644 --- a/java/core/src/test/java/com/google/protobuf/ParseExceptionsTest.java +++ b/java/core/src/test/java/com/google/protobuf/ParseExceptionsTest.java @@ -1,18 +1,52 @@ -package com.google.protobuf; +// 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. -import com.google.protobuf.DescriptorProtos.DescriptorProto; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import org.junit.Test; +package com.google.protobuf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.google.protobuf.DescriptorProtos.DescriptorProto; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + /** * Tests the exceptions thrown when parsing from a stream. The methods on the {@link Parser} * interface are specified to only throw {@link InvalidProtocolBufferException}. But we really want @@ -22,6 +56,7 @@ import static org.junit.Assert.fail; * * @author jh@squareup.com (Joshua Humphries) */ +@RunWith(JUnit4.class) public class ParseExceptionsTest { private interface ParseTester { @@ -46,116 +81,143 @@ public class ParseExceptionsTest { @Test public void message_parseFrom_InputStream() { setup(); - verifyExceptions(new ParseTester() { - public DescriptorProto parse(InputStream in) throws IOException { - return DescriptorProto.parseFrom(in); - } - }); + verifyExceptions( + new ParseTester() { + @Override + public DescriptorProto parse(InputStream in) throws IOException { + return DescriptorProto.parseFrom(in); + } + }); } @Test public void message_parseFrom_InputStreamAndExtensionRegistry() { setup(); - verifyExceptions(new ParseTester() { - public DescriptorProto parse(InputStream in) throws IOException { - return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance()); - } - }); + verifyExceptions( + new ParseTester() { + @Override + public DescriptorProto parse(InputStream in) throws IOException { + return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance()); + } + }); } @Test public void message_parseFrom_CodedInputStream() { setup(); - verifyExceptions(new ParseTester() { - public DescriptorProto parse(InputStream in) throws IOException { - return DescriptorProto.parseFrom(CodedInputStream.newInstance(in)); - } - }); + verifyExceptions( + new ParseTester() { + @Override + public DescriptorProto parse(InputStream in) throws IOException { + return DescriptorProto.parseFrom(CodedInputStream.newInstance(in)); + } + }); } @Test public void message_parseFrom_CodedInputStreamAndExtensionRegistry() { setup(); - verifyExceptions(new ParseTester() { - public DescriptorProto parse(InputStream in) throws IOException { - return DescriptorProto.parseFrom(CodedInputStream.newInstance(in), - ExtensionRegistry.newInstance()); - } - }); + verifyExceptions( + new ParseTester() { + @Override + public DescriptorProto parse(InputStream in) throws IOException { + return DescriptorProto.parseFrom( + CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()); + } + }); } @Test public void message_parseDelimitedFrom_InputStream() { setupDelimited(); - verifyExceptions(new ParseTester() { - public DescriptorProto parse(InputStream in) throws IOException { - return DescriptorProto.parseDelimitedFrom(in); - } - }); + verifyExceptions( + new ParseTester() { + @Override + public DescriptorProto parse(InputStream in) throws IOException { + return DescriptorProto.parseDelimitedFrom(in); + } + }); } @Test public void message_parseDelimitedFrom_InputStreamAndExtensionRegistry() { setupDelimited(); - verifyExceptions(new ParseTester() { - public DescriptorProto parse(InputStream in) throws IOException { - return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance()); - } - }); + verifyExceptions( + new ParseTester() { + @Override + public DescriptorProto parse(InputStream in) throws IOException { + return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance()); + } + }); } @Test public void messageBuilder_mergeFrom_InputStream() { setup(); - verifyExceptions(new ParseTester() { - public DescriptorProto parse(InputStream in) throws IOException { - return DescriptorProto.newBuilder().mergeFrom(in).build(); - } - }); + verifyExceptions( + new ParseTester() { + @Override + public DescriptorProto parse(InputStream in) throws IOException { + return DescriptorProto.newBuilder().mergeFrom(in).build(); + } + }); } @Test public void messageBuilder_mergeFrom_InputStreamAndExtensionRegistry() { setup(); - verifyExceptions(new ParseTester() { - public DescriptorProto parse(InputStream in) throws IOException { - return DescriptorProto.newBuilder().mergeFrom(in, ExtensionRegistry.newInstance()).build(); - } - }); + verifyExceptions( + new ParseTester() { + @Override + public DescriptorProto parse(InputStream in) throws IOException { + return DescriptorProto.newBuilder() + .mergeFrom(in, ExtensionRegistry.newInstance()) + .build(); + } + }); } @Test public void messageBuilder_mergeFrom_CodedInputStream() { setup(); - verifyExceptions(new ParseTester() { - public DescriptorProto parse(InputStream in) throws IOException { - return DescriptorProto.newBuilder().mergeFrom(CodedInputStream.newInstance(in)).build(); - } - }); + verifyExceptions( + new ParseTester() { + @Override + public DescriptorProto parse(InputStream in) throws IOException { + return DescriptorProto.newBuilder().mergeFrom(CodedInputStream.newInstance(in)).build(); + } + }); } @Test public void messageBuilder_mergeFrom_CodedInputStreamAndExtensionRegistry() { setup(); - verifyExceptions(new ParseTester() { - public DescriptorProto parse(InputStream in) throws IOException { - return DescriptorProto.newBuilder() - .mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()).build(); - } - }); + verifyExceptions( + new ParseTester() { + @Override + public DescriptorProto parse(InputStream in) throws IOException { + return DescriptorProto.newBuilder() + .mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()) + .build(); + } + }); } @Test public void messageBuilder_mergeDelimitedFrom_InputStream() { setupDelimited(); - verifyExceptions(new ParseTester() { - public DescriptorProto parse(InputStream in) throws IOException { - DescriptorProto.Builder builder = DescriptorProto.newBuilder(); - builder.mergeDelimitedFrom(in); - return builder.build(); - } - }); + verifyExceptions( + new ParseTester() { + @Override + public DescriptorProto parse(InputStream in) throws IOException { + DescriptorProto.Builder builder = DescriptorProto.newBuilder(); + builder.mergeDelimitedFrom(in); + return builder.build(); + } + }); } @Test public void messageBuilder_mergeDelimitedFrom_InputStreamAndExtensionRegistry() { setupDelimited(); - verifyExceptions(new ParseTester() { - public DescriptorProto parse(InputStream in) throws IOException { - DescriptorProto.Builder builder = DescriptorProto.newBuilder(); - builder.mergeDelimitedFrom(in, ExtensionRegistry.newInstance()); - return builder.build(); - } - }); + verifyExceptions( + new ParseTester() { + @Override + public DescriptorProto parse(InputStream in) throws IOException { + DescriptorProto.Builder builder = DescriptorProto.newBuilder(); + builder.mergeDelimitedFrom(in, ExtensionRegistry.newInstance()); + return builder.build(); + } + }); } private void verifyExceptions(ParseTester parseTester) { diff --git a/java/core/src/test/java/com/google/protobuf/ParserTest.java b/java/core/src/test/java/com/google/protobuf/ParserTest.java index 5a92bacfb..30842d2c0 100644 --- a/java/core/src/test/java/com/google/protobuf/ParserTest.java +++ b/java/core/src/test/java/com/google/protobuf/ParserTest.java @@ -33,15 +33,15 @@ package com.google.protobuf; import com.google.protobuf.UnittestLite.TestAllTypesLite; import com.google.protobuf.UnittestLite.TestPackedExtensionsLite; import com.google.protobuf.UnittestLite.TestParsingMergeLite; +import protobuf_unittest.UnittestOptimizeFor; import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize; import protobuf_unittest.UnittestOptimizeFor.TestRequiredOptimizedForSize; -import protobuf_unittest.UnittestOptimizeFor; +import protobuf_unittest.UnittestProto; import protobuf_unittest.UnittestProto.ForeignMessage; import protobuf_unittest.UnittestProto.TestAllTypes; import protobuf_unittest.UnittestProto.TestEmptyMessage; import protobuf_unittest.UnittestProto.TestParsingMerge; import protobuf_unittest.UnittestProto.TestRequired; -import protobuf_unittest.UnittestProto; import junit.framework.TestCase; @@ -179,16 +179,16 @@ public class ParserTest extends TestCase { public void testParseExtensions() throws Exception { assertRoundTripEquals(TestUtil.getAllExtensionsSet(), TestUtil.getExtensionRegistry()); - assertRoundTripEquals(TestUtil.getAllLiteExtensionsSet(), - TestUtil.getExtensionRegistryLite()); + assertRoundTripEquals( + TestUtilLite.getAllLiteExtensionsSet(), TestUtilLite.getExtensionRegistryLite()); } public void testParsePacked() throws Exception { assertRoundTripEquals(TestUtil.getPackedSet()); assertRoundTripEquals(TestUtil.getPackedExtensionsSet(), TestUtil.getExtensionRegistry()); - assertRoundTripEquals(TestUtil.getLitePackedExtensionsSet(), - TestUtil.getExtensionRegistryLite()); + assertRoundTripEquals( + TestUtilLite.getLitePackedExtensionsSet(), TestUtilLite.getExtensionRegistryLite()); } public void testParseDelimitedTo() throws Exception { @@ -198,8 +198,7 @@ public class ParserTest extends TestCase { normalMessage.writeDelimitedTo(output); // Write MessageLite with packed extension fields. - TestPackedExtensionsLite packedMessage = - TestUtil.getLitePackedExtensionsSet(); + TestPackedExtensionsLite packedMessage = TestUtilLite.getLitePackedExtensionsSet(); packedMessage.writeDelimitedTo(output); InputStream input = new ByteArrayInputStream(output.toByteArray()); @@ -208,8 +207,9 @@ public class ParserTest extends TestCase { normalMessage.getParserForType().parseDelimitedFrom(input)); assertMessageEquals( packedMessage, - packedMessage.getParserForType().parseDelimitedFrom( - input, TestUtil.getExtensionRegistryLite())); + packedMessage + .getParserForType() + .parseDelimitedFrom(input, TestUtilLite.getExtensionRegistryLite())); } public void testParseUnknownFields() throws Exception { diff --git a/java/core/src/test/java/com/google/protobuf/ProtobufArrayListTest.java b/java/core/src/test/java/com/google/protobuf/ProtobufArrayListTest.java index 245c3dee4..3f45e226a 100644 --- a/java/core/src/test/java/com/google/protobuf/ProtobufArrayListTest.java +++ b/java/core/src/test/java/com/google/protobuf/ProtobufArrayListTest.java @@ -63,20 +63,6 @@ public class ProtobufArrayListTest extends TestCase { assertImmutable(ProtobufArrayList.emptyList()); } - public void testCopyConstructor() { - ProtobufArrayList copy = new ProtobufArrayList(TERTIARY_LIST); - assertEquals(TERTIARY_LIST, copy); - - copy = new ProtobufArrayList(IntArrayList.emptyList()); - assertEquals(ProtobufArrayList.emptyList(), copy); - - copy = new ProtobufArrayList(asList(1, 2, 3)); - assertEquals(asList(1, 2, 3), copy); - - copy = new ProtobufArrayList(Collections.emptyList()); - assertEquals(ProtobufArrayList.emptyList(), copy); - } - public void testModificationWithIteration() { list.addAll(asList(1, 2, 3, 4)); Iterator iterator = list.iterator(); diff --git a/java/core/src/test/java/com/google/protobuf/ServiceTest.java b/java/core/src/test/java/com/google/protobuf/ServiceTest.java index ff980d66d..b902737de 100644 --- a/java/core/src/test/java/com/google/protobuf/ServiceTest.java +++ b/java/core/src/test/java/com/google/protobuf/ServiceTest.java @@ -35,22 +35,22 @@ import com.google.protobuf.Descriptors.MethodDescriptor; import google.protobuf.no_generic_services_test.UnittestNoGenericServices; import protobuf_unittest.MessageWithNoOuter; import protobuf_unittest.ServiceWithNoOuter; -import protobuf_unittest.UnittestProto.TestAllTypes; -import protobuf_unittest.UnittestProto.TestService; -import protobuf_unittest.UnittestProto.FooRequest; -import protobuf_unittest.UnittestProto.FooResponse; import protobuf_unittest.UnittestProto.BarRequest; import protobuf_unittest.UnittestProto.BarResponse; +import protobuf_unittest.UnittestProto.FooRequest; +import protobuf_unittest.UnittestProto.FooResponse; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestService; + +import junit.framework.TestCase; import org.easymock.classextension.EasyMock; -import org.easymock.classextension.IMocksControl; import org.easymock.IArgumentMatcher; +import org.easymock.classextension.IMocksControl; import java.util.HashSet; import java.util.Set; -import junit.framework.TestCase; - /** * Tests services and stubs. * @@ -175,12 +175,14 @@ public class ServiceTest extends TestCase { MethodDescriptor fooMethod = ServiceWithNoOuter.getDescriptor().findMethodByName("Foo"); MessageWithNoOuter request = MessageWithNoOuter.getDefaultInstance(); - RpcCallback callback = new RpcCallback() { - public void run(Message parameter) { - // No reason this should be run. - fail(); - } - }; + RpcCallback callback = + new RpcCallback() { + @Override + public void run(Message parameter) { + // No reason this should be run. + fail(); + } + }; RpcCallback specializedCallback = RpcUtil.specializeCallback(callback); @@ -290,7 +292,9 @@ public class ServiceTest extends TestCase { public boolean isCalled() { return called; } public void reset() { called = false; } - public void run(Type message) { called = true; } + @Override + public void run(Type message) { + called = true; } } /** Implementation of the wrapsCallback() argument matcher. */ @@ -301,6 +305,7 @@ public class ServiceTest extends TestCase { this.callback = callback; } + @Override @SuppressWarnings("unchecked") public boolean matches(Object actual) { if (!(actual instanceof RpcCallback)) { @@ -313,6 +318,7 @@ public class ServiceTest extends TestCase { return callback.isCalled(); } + @Override public void appendTo(StringBuffer buffer) { buffer.append("wrapsCallback(mockCallback)"); } diff --git a/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java b/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java index 366086d30..e96ecd65d 100644 --- a/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java +++ b/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java @@ -56,14 +56,17 @@ public class SmallSortedMapTest extends TestCase { this.value = value; } + @Override public K getKey() { return key; } + @Override public V getValue() { return value; } + @Override public V setValue(V value) { V oldValue = this.value; this.value = value; 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 01acb8845..08b2a76d9 100644 --- a/java/core/src/test/java/com/google/protobuf/TestUtil.java +++ b/java/core/src/test/java/com/google/protobuf/TestUtil.java @@ -30,205 +30,200 @@ package com.google.protobuf; -import protobuf_unittest.UnittestProto; -import com.google.protobuf.UnittestLite; - +import static com.google.protobuf.UnittestLite.defaultBoolExtensionLite; +import static com.google.protobuf.UnittestLite.defaultBytesExtensionLite; +import static com.google.protobuf.UnittestLite.defaultCordExtensionLite; +import static com.google.protobuf.UnittestLite.defaultDoubleExtensionLite; +import static com.google.protobuf.UnittestLite.defaultFixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultFixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultFloatExtensionLite; +import static com.google.protobuf.UnittestLite.defaultForeignEnumExtensionLite; +import static com.google.protobuf.UnittestLite.defaultImportEnumExtensionLite; +import static com.google.protobuf.UnittestLite.defaultInt32ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultInt64ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultNestedEnumExtensionLite; +import static com.google.protobuf.UnittestLite.defaultSfixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultSfixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultSint32ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultSint64ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultStringExtensionLite; +import static com.google.protobuf.UnittestLite.defaultStringPieceExtensionLite; +import static com.google.protobuf.UnittestLite.defaultUint32ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultUint64ExtensionLite; +import static com.google.protobuf.UnittestLite.oneofBytesExtensionLite; +import static com.google.protobuf.UnittestLite.oneofNestedMessageExtensionLite; +import static com.google.protobuf.UnittestLite.oneofStringExtensionLite; +import static com.google.protobuf.UnittestLite.oneofUint32ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalBoolExtensionLite; +import static com.google.protobuf.UnittestLite.optionalBytesExtensionLite; +import static com.google.protobuf.UnittestLite.optionalCordExtensionLite; +import static com.google.protobuf.UnittestLite.optionalDoubleExtensionLite; +import static com.google.protobuf.UnittestLite.optionalFixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalFixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalFloatExtensionLite; +import static com.google.protobuf.UnittestLite.optionalForeignEnumExtensionLite; +import static com.google.protobuf.UnittestLite.optionalForeignMessageExtensionLite; +import static com.google.protobuf.UnittestLite.optionalGroupExtensionLite; +import static com.google.protobuf.UnittestLite.optionalImportEnumExtensionLite; +import static com.google.protobuf.UnittestLite.optionalImportMessageExtensionLite; +import static com.google.protobuf.UnittestLite.optionalInt32ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalInt64ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalLazyMessageExtensionLite; +import static com.google.protobuf.UnittestLite.optionalNestedEnumExtensionLite; +import static com.google.protobuf.UnittestLite.optionalNestedMessageExtensionLite; +import static com.google.protobuf.UnittestLite.optionalPublicImportMessageExtensionLite; +import static com.google.protobuf.UnittestLite.optionalSfixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalSfixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalSint32ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalSint64ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalStringExtensionLite; +import static com.google.protobuf.UnittestLite.optionalStringPieceExtensionLite; +import static com.google.protobuf.UnittestLite.optionalUint32ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalUint64ExtensionLite; +import static com.google.protobuf.UnittestLite.packedBoolExtensionLite; +import static com.google.protobuf.UnittestLite.packedDoubleExtensionLite; +import static com.google.protobuf.UnittestLite.packedEnumExtensionLite; +import static com.google.protobuf.UnittestLite.packedFixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.packedFixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.packedFloatExtensionLite; +import static com.google.protobuf.UnittestLite.packedInt32ExtensionLite; +import static com.google.protobuf.UnittestLite.packedInt64ExtensionLite; +import static com.google.protobuf.UnittestLite.packedSfixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.packedSfixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.packedSint32ExtensionLite; +import static com.google.protobuf.UnittestLite.packedSint64ExtensionLite; +import static com.google.protobuf.UnittestLite.packedUint32ExtensionLite; +import static com.google.protobuf.UnittestLite.packedUint64ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedBoolExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedBytesExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedCordExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedDoubleExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedFixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedFixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedFloatExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedForeignEnumExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedForeignMessageExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedGroupExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedImportEnumExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedImportMessageExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedInt32ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedInt64ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedLazyMessageExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedNestedEnumExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedNestedMessageExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedSfixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedSfixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedSint32ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedSint64ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedStringExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedStringPieceExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedUint32ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedUint64ExtensionLite; +import static protobuf_unittest.UnittestProto.OptionalGroup_extension; +import static protobuf_unittest.UnittestProto.RepeatedGroup_extension; +import static protobuf_unittest.UnittestProto.defaultBoolExtension; +import static protobuf_unittest.UnittestProto.defaultBytesExtension; +import static protobuf_unittest.UnittestProto.defaultCordExtension; +import static protobuf_unittest.UnittestProto.defaultDoubleExtension; +import static protobuf_unittest.UnittestProto.defaultFixed32Extension; +import static protobuf_unittest.UnittestProto.defaultFixed64Extension; +import static protobuf_unittest.UnittestProto.defaultFloatExtension; +import static protobuf_unittest.UnittestProto.defaultForeignEnumExtension; +import static protobuf_unittest.UnittestProto.defaultImportEnumExtension; // The static imports are to avoid 100+ char lines. The following is roughly equivalent to // import static protobuf_unittest.UnittestProto.*; import static protobuf_unittest.UnittestProto.defaultInt32Extension; import static protobuf_unittest.UnittestProto.defaultInt64Extension; -import static protobuf_unittest.UnittestProto.defaultUint32Extension; -import static protobuf_unittest.UnittestProto.defaultUint64Extension; -import static protobuf_unittest.UnittestProto.defaultSint32Extension; -import static protobuf_unittest.UnittestProto.defaultSint64Extension; -import static protobuf_unittest.UnittestProto.defaultFixed32Extension; -import static protobuf_unittest.UnittestProto.defaultFixed64Extension; +import static protobuf_unittest.UnittestProto.defaultNestedEnumExtension; import static protobuf_unittest.UnittestProto.defaultSfixed32Extension; import static protobuf_unittest.UnittestProto.defaultSfixed64Extension; -import static protobuf_unittest.UnittestProto.defaultFloatExtension; -import static protobuf_unittest.UnittestProto.defaultDoubleExtension; -import static protobuf_unittest.UnittestProto.defaultBoolExtension; +import static protobuf_unittest.UnittestProto.defaultSint32Extension; +import static protobuf_unittest.UnittestProto.defaultSint64Extension; import static protobuf_unittest.UnittestProto.defaultStringExtension; -import static protobuf_unittest.UnittestProto.defaultBytesExtension; -import static protobuf_unittest.UnittestProto.defaultNestedEnumExtension; -import static protobuf_unittest.UnittestProto.defaultForeignEnumExtension; -import static protobuf_unittest.UnittestProto.defaultImportEnumExtension; import static protobuf_unittest.UnittestProto.defaultStringPieceExtension; -import static protobuf_unittest.UnittestProto.defaultCordExtension; - -import static protobuf_unittest.UnittestProto.oneofUint32Extension; +import static protobuf_unittest.UnittestProto.defaultUint32Extension; +import static protobuf_unittest.UnittestProto.defaultUint64Extension; +import static protobuf_unittest.UnittestProto.oneofBytesExtension; import static protobuf_unittest.UnittestProto.oneofNestedMessageExtension; import static protobuf_unittest.UnittestProto.oneofStringExtension; -import static protobuf_unittest.UnittestProto.oneofBytesExtension; - -import static protobuf_unittest.UnittestProto.optionalInt32Extension; -import static protobuf_unittest.UnittestProto.optionalInt64Extension; -import static protobuf_unittest.UnittestProto.optionalUint32Extension; -import static protobuf_unittest.UnittestProto.optionalUint64Extension; -import static protobuf_unittest.UnittestProto.optionalSint32Extension; -import static protobuf_unittest.UnittestProto.optionalSint64Extension; +import static protobuf_unittest.UnittestProto.oneofUint32Extension; +import static protobuf_unittest.UnittestProto.optionalBoolExtension; +import static protobuf_unittest.UnittestProto.optionalBytesExtension; +import static protobuf_unittest.UnittestProto.optionalCordExtension; +import static protobuf_unittest.UnittestProto.optionalDoubleExtension; import static protobuf_unittest.UnittestProto.optionalFixed32Extension; import static protobuf_unittest.UnittestProto.optionalFixed64Extension; -import static protobuf_unittest.UnittestProto.optionalSfixed32Extension; -import static protobuf_unittest.UnittestProto.optionalSfixed64Extension; import static protobuf_unittest.UnittestProto.optionalFloatExtension; -import static protobuf_unittest.UnittestProto.optionalDoubleExtension; -import static protobuf_unittest.UnittestProto.optionalBoolExtension; -import static protobuf_unittest.UnittestProto.optionalStringExtension; -import static protobuf_unittest.UnittestProto.optionalBytesExtension; -import static protobuf_unittest.UnittestProto.optionalGroupExtension; -import static protobuf_unittest.UnittestProto.optionalCordExtension; import static protobuf_unittest.UnittestProto.optionalForeignEnumExtension; import static protobuf_unittest.UnittestProto.optionalForeignMessageExtension; +import static protobuf_unittest.UnittestProto.optionalGroupExtension; import static protobuf_unittest.UnittestProto.optionalImportEnumExtension; import static protobuf_unittest.UnittestProto.optionalImportMessageExtension; +import static protobuf_unittest.UnittestProto.optionalInt32Extension; +import static protobuf_unittest.UnittestProto.optionalInt64Extension; +import static protobuf_unittest.UnittestProto.optionalLazyMessageExtension; import static protobuf_unittest.UnittestProto.optionalNestedEnumExtension; import static protobuf_unittest.UnittestProto.optionalNestedMessageExtension; import static protobuf_unittest.UnittestProto.optionalPublicImportMessageExtension; -import static protobuf_unittest.UnittestProto.optionalLazyMessageExtension; +import static protobuf_unittest.UnittestProto.optionalSfixed32Extension; +import static protobuf_unittest.UnittestProto.optionalSfixed64Extension; +import static protobuf_unittest.UnittestProto.optionalSint32Extension; +import static protobuf_unittest.UnittestProto.optionalSint64Extension; +import static protobuf_unittest.UnittestProto.optionalStringExtension; import static protobuf_unittest.UnittestProto.optionalStringPieceExtension; - -import static protobuf_unittest.UnittestProto.repeatedInt32Extension; -import static protobuf_unittest.UnittestProto.repeatedInt64Extension; -import static protobuf_unittest.UnittestProto.repeatedUint32Extension; -import static protobuf_unittest.UnittestProto.repeatedUint64Extension; -import static protobuf_unittest.UnittestProto.repeatedSint32Extension; -import static protobuf_unittest.UnittestProto.repeatedSint64Extension; -import static protobuf_unittest.UnittestProto.repeatedFixed32Extension; -import static protobuf_unittest.UnittestProto.repeatedFixed64Extension; -import static protobuf_unittest.UnittestProto.repeatedSfixed32Extension; -import static protobuf_unittest.UnittestProto.repeatedSfixed64Extension; -import static protobuf_unittest.UnittestProto.repeatedFloatExtension; -import static protobuf_unittest.UnittestProto.repeatedDoubleExtension; -import static protobuf_unittest.UnittestProto.repeatedBoolExtension; -import static protobuf_unittest.UnittestProto.repeatedStringExtension; -import static protobuf_unittest.UnittestProto.repeatedBytesExtension; -import static protobuf_unittest.UnittestProto.repeatedGroupExtension; -import static protobuf_unittest.UnittestProto.repeatedNestedMessageExtension; -import static protobuf_unittest.UnittestProto.repeatedForeignMessageExtension; -import static protobuf_unittest.UnittestProto.repeatedImportMessageExtension; -import static protobuf_unittest.UnittestProto.repeatedLazyMessageExtension; -import static protobuf_unittest.UnittestProto.repeatedNestedEnumExtension; -import static protobuf_unittest.UnittestProto.repeatedForeignEnumExtension; -import static protobuf_unittest.UnittestProto.repeatedImportEnumExtension; -import static protobuf_unittest.UnittestProto.repeatedStringPieceExtension; -import static protobuf_unittest.UnittestProto.repeatedCordExtension; - -import static protobuf_unittest.UnittestProto.OptionalGroup_extension; -import static protobuf_unittest.UnittestProto.RepeatedGroup_extension; - -import static protobuf_unittest.UnittestProto.packedInt32Extension; -import static protobuf_unittest.UnittestProto.packedInt64Extension; -import static protobuf_unittest.UnittestProto.packedUint32Extension; -import static protobuf_unittest.UnittestProto.packedUint64Extension; -import static protobuf_unittest.UnittestProto.packedSint32Extension; -import static protobuf_unittest.UnittestProto.packedSint64Extension; +import static protobuf_unittest.UnittestProto.optionalUint32Extension; +import static protobuf_unittest.UnittestProto.optionalUint64Extension; +import static protobuf_unittest.UnittestProto.packedBoolExtension; +import static protobuf_unittest.UnittestProto.packedDoubleExtension; +import static protobuf_unittest.UnittestProto.packedEnumExtension; import static protobuf_unittest.UnittestProto.packedFixed32Extension; import static protobuf_unittest.UnittestProto.packedFixed64Extension; +import static protobuf_unittest.UnittestProto.packedFloatExtension; +import static protobuf_unittest.UnittestProto.packedInt32Extension; +import static protobuf_unittest.UnittestProto.packedInt64Extension; import static protobuf_unittest.UnittestProto.packedSfixed32Extension; import static protobuf_unittest.UnittestProto.packedSfixed64Extension; -import static protobuf_unittest.UnittestProto.packedFloatExtension; -import static protobuf_unittest.UnittestProto.packedDoubleExtension; -import static protobuf_unittest.UnittestProto.packedBoolExtension; -import static protobuf_unittest.UnittestProto.packedEnumExtension; - -import static com.google.protobuf.UnittestLite.defaultInt32ExtensionLite; -import static com.google.protobuf.UnittestLite.defaultInt64ExtensionLite; -import static com.google.protobuf.UnittestLite.defaultUint32ExtensionLite; -import static com.google.protobuf.UnittestLite.defaultUint64ExtensionLite; -import static com.google.protobuf.UnittestLite.defaultSint32ExtensionLite; -import static com.google.protobuf.UnittestLite.defaultSint64ExtensionLite; -import static com.google.protobuf.UnittestLite.defaultFixed32ExtensionLite; -import static com.google.protobuf.UnittestLite.defaultFixed64ExtensionLite; -import static com.google.protobuf.UnittestLite.defaultSfixed32ExtensionLite; -import static com.google.protobuf.UnittestLite.defaultSfixed64ExtensionLite; -import static com.google.protobuf.UnittestLite.defaultFloatExtensionLite; -import static com.google.protobuf.UnittestLite.defaultDoubleExtensionLite; -import static com.google.protobuf.UnittestLite.defaultBoolExtensionLite; -import static com.google.protobuf.UnittestLite.defaultStringExtensionLite; -import static com.google.protobuf.UnittestLite.defaultBytesExtensionLite; -import static com.google.protobuf.UnittestLite.defaultNestedEnumExtensionLite; -import static com.google.protobuf.UnittestLite.defaultForeignEnumExtensionLite; -import static com.google.protobuf.UnittestLite.defaultImportEnumExtensionLite; -import static com.google.protobuf.UnittestLite.defaultStringPieceExtensionLite; -import static com.google.protobuf.UnittestLite.defaultCordExtensionLite; - -import static com.google.protobuf.UnittestLite.oneofUint32ExtensionLite; -import static com.google.protobuf.UnittestLite.oneofNestedMessageExtensionLite; -import static com.google.protobuf.UnittestLite.oneofStringExtensionLite; -import static com.google.protobuf.UnittestLite.oneofBytesExtensionLite; - -import static com.google.protobuf.UnittestLite.optionalInt32ExtensionLite; -import static com.google.protobuf.UnittestLite.optionalInt64ExtensionLite; -import static com.google.protobuf.UnittestLite.optionalUint32ExtensionLite; -import static com.google.protobuf.UnittestLite.optionalUint64ExtensionLite; -import static com.google.protobuf.UnittestLite.optionalSint32ExtensionLite; -import static com.google.protobuf.UnittestLite.optionalSint64ExtensionLite; -import static com.google.protobuf.UnittestLite.optionalFixed32ExtensionLite; -import static com.google.protobuf.UnittestLite.optionalFixed64ExtensionLite; -import static com.google.protobuf.UnittestLite.optionalSfixed32ExtensionLite; -import static com.google.protobuf.UnittestLite.optionalSfixed64ExtensionLite; -import static com.google.protobuf.UnittestLite.optionalFloatExtensionLite; -import static com.google.protobuf.UnittestLite.optionalDoubleExtensionLite; -import static com.google.protobuf.UnittestLite.optionalBoolExtensionLite; -import static com.google.protobuf.UnittestLite.optionalStringExtensionLite; -import static com.google.protobuf.UnittestLite.optionalBytesExtensionLite; -import static com.google.protobuf.UnittestLite.optionalGroupExtensionLite; -import static com.google.protobuf.UnittestLite.optionalNestedMessageExtensionLite; -import static com.google.protobuf.UnittestLite.optionalForeignEnumExtensionLite; -import static com.google.protobuf.UnittestLite.optionalForeignMessageExtensionLite; -import static com.google.protobuf.UnittestLite.optionalImportEnumExtensionLite; -import static com.google.protobuf.UnittestLite.optionalImportMessageExtensionLite; -import static com.google.protobuf.UnittestLite.optionalNestedEnumExtensionLite; -import static com.google.protobuf.UnittestLite.optionalPublicImportMessageExtensionLite; -import static com.google.protobuf.UnittestLite.optionalLazyMessageExtensionLite; -import static com.google.protobuf.UnittestLite.optionalStringPieceExtensionLite; -import static com.google.protobuf.UnittestLite.optionalCordExtensionLite; - -import static com.google.protobuf.UnittestLite.repeatedInt32ExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedInt64ExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedUint32ExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedUint64ExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedSint32ExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedSint64ExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedFixed32ExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedFixed64ExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedSfixed32ExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedSfixed64ExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedFloatExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedDoubleExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedBoolExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedStringExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedBytesExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedGroupExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedNestedMessageExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedForeignMessageExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedImportMessageExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedLazyMessageExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedNestedEnumExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedForeignEnumExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedImportEnumExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedStringPieceExtensionLite; -import static com.google.protobuf.UnittestLite.repeatedCordExtensionLite; - -import static com.google.protobuf.UnittestLite.OptionalGroup_extension_lite; -import static com.google.protobuf.UnittestLite.RepeatedGroup_extension_lite; - -import static com.google.protobuf.UnittestLite.packedInt32ExtensionLite; -import static com.google.protobuf.UnittestLite.packedInt64ExtensionLite; -import static com.google.protobuf.UnittestLite.packedUint32ExtensionLite; -import static com.google.protobuf.UnittestLite.packedUint64ExtensionLite; -import static com.google.protobuf.UnittestLite.packedSint32ExtensionLite; -import static com.google.protobuf.UnittestLite.packedSint64ExtensionLite; -import static com.google.protobuf.UnittestLite.packedFixed32ExtensionLite; -import static com.google.protobuf.UnittestLite.packedFixed64ExtensionLite; -import static com.google.protobuf.UnittestLite.packedSfixed32ExtensionLite; -import static com.google.protobuf.UnittestLite.packedSfixed64ExtensionLite; -import static com.google.protobuf.UnittestLite.packedFloatExtensionLite; -import static com.google.protobuf.UnittestLite.packedDoubleExtensionLite; -import static com.google.protobuf.UnittestLite.packedBoolExtensionLite; -import static com.google.protobuf.UnittestLite.packedEnumExtensionLite; +import static protobuf_unittest.UnittestProto.packedSint32Extension; +import static protobuf_unittest.UnittestProto.packedSint64Extension; +import static protobuf_unittest.UnittestProto.packedUint32Extension; +import static protobuf_unittest.UnittestProto.packedUint64Extension; +import static protobuf_unittest.UnittestProto.repeatedBoolExtension; +import static protobuf_unittest.UnittestProto.repeatedBytesExtension; +import static protobuf_unittest.UnittestProto.repeatedCordExtension; +import static protobuf_unittest.UnittestProto.repeatedDoubleExtension; +import static protobuf_unittest.UnittestProto.repeatedFixed32Extension; +import static protobuf_unittest.UnittestProto.repeatedFixed64Extension; +import static protobuf_unittest.UnittestProto.repeatedFloatExtension; +import static protobuf_unittest.UnittestProto.repeatedForeignEnumExtension; +import static protobuf_unittest.UnittestProto.repeatedForeignMessageExtension; +import static protobuf_unittest.UnittestProto.repeatedGroupExtension; +import static protobuf_unittest.UnittestProto.repeatedImportEnumExtension; +import static protobuf_unittest.UnittestProto.repeatedImportMessageExtension; +import static protobuf_unittest.UnittestProto.repeatedInt32Extension; +import static protobuf_unittest.UnittestProto.repeatedInt64Extension; +import static protobuf_unittest.UnittestProto.repeatedLazyMessageExtension; +import static protobuf_unittest.UnittestProto.repeatedNestedEnumExtension; +import static protobuf_unittest.UnittestProto.repeatedNestedMessageExtension; +import static protobuf_unittest.UnittestProto.repeatedSfixed32Extension; +import static protobuf_unittest.UnittestProto.repeatedSfixed64Extension; +import static protobuf_unittest.UnittestProto.repeatedSint32Extension; +import static protobuf_unittest.UnittestProto.repeatedSint64Extension; +import static protobuf_unittest.UnittestProto.repeatedStringExtension; +import static protobuf_unittest.UnittestProto.repeatedStringPieceExtension; +import static protobuf_unittest.UnittestProto.repeatedUint32Extension; +import static protobuf_unittest.UnittestProto.repeatedUint64Extension; +import com.google.protobuf.UnittestImportLite.ImportEnumLite; +import com.google.protobuf.UnittestLite.ForeignEnumLite; +import com.google.protobuf.UnittestLite.TestAllExtensionsLiteOrBuilder; +import com.google.protobuf.UnittestLite.TestAllTypesLite; +import com.google.protobuf.UnittestLite.TestPackedExtensionsLite; +import com.google.protobuf.test.UnittestImport.ImportEnum; +import com.google.protobuf.test.UnittestImport.ImportMessage; +import com.google.protobuf.test.UnittestImportPublic.PublicImportMessage; +import protobuf_unittest.UnittestProto; +import protobuf_unittest.UnittestProto.ForeignEnum; +import protobuf_unittest.UnittestProto.ForeignMessage; import protobuf_unittest.UnittestProto.TestAllExtensions; import protobuf_unittest.UnittestProto.TestAllExtensionsOrBuilder; import protobuf_unittest.UnittestProto.TestAllTypes; @@ -237,21 +232,6 @@ import protobuf_unittest.UnittestProto.TestOneof2; import protobuf_unittest.UnittestProto.TestPackedExtensions; import protobuf_unittest.UnittestProto.TestPackedTypes; import protobuf_unittest.UnittestProto.TestUnpackedTypes; -import protobuf_unittest.UnittestProto.ForeignMessage; -import protobuf_unittest.UnittestProto.ForeignEnum; -import com.google.protobuf.test.UnittestImport.ImportEnum; -import com.google.protobuf.test.UnittestImport.ImportMessage; -import com.google.protobuf.test.UnittestImportPublic.PublicImportMessage; - -import com.google.protobuf.UnittestLite.TestAllTypesLite; -import com.google.protobuf.UnittestLite.TestAllExtensionsLite; -import com.google.protobuf.UnittestLite.TestAllExtensionsLiteOrBuilder; -import com.google.protobuf.UnittestLite.TestPackedExtensionsLite; -import com.google.protobuf.UnittestLite.ForeignMessageLite; -import com.google.protobuf.UnittestLite.ForeignEnumLite; -import com.google.protobuf.UnittestImportLite.ImportEnumLite; -import com.google.protobuf.UnittestImportLite.ImportMessageLite; -import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite; import junit.framework.Assert; @@ -299,16 +279,6 @@ public final class TestUtil { return builder; } - /** - * Get a {@code TestAllTypesLite.Builder} with all fields set as they would be by - * {@link #setAllFields(TestAllTypesLite.Builder)}. - */ - public static TestAllTypesLite.Builder getAllLiteSetBuilder() { - TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder(); - setAllFields(builder); - return builder; - } - /** * Get a {@code TestAllExtensions} with all fields set as they would be by * {@link #setAllExtensions(TestAllExtensions.Builder)}. @@ -319,12 +289,6 @@ public final class TestUtil { return builder.build(); } - public static TestAllExtensionsLite getAllLiteExtensionsSet() { - TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder(); - setAllExtensions(builder); - return builder.build(); - } - public static TestPackedTypes getPackedSet() { TestPackedTypes.Builder builder = TestPackedTypes.newBuilder(); setPackedFields(builder); @@ -343,157 +307,6 @@ public final class TestUtil { return builder.build(); } - public static TestPackedExtensionsLite getLitePackedExtensionsSet() { - TestPackedExtensionsLite.Builder builder = - TestPackedExtensionsLite.newBuilder(); - setPackedExtensions(builder); - return builder.build(); - } - - /** - * Set every field of {@code builder} to the values expected by - * {@code assertAllFieldsSet()}. - */ - public static void setAllFields(TestAllTypesLite.Builder builder) { - builder.setOptionalInt32 (101); - builder.setOptionalInt64 (102); - builder.setOptionalUint32 (103); - builder.setOptionalUint64 (104); - builder.setOptionalSint32 (105); - builder.setOptionalSint64 (106); - builder.setOptionalFixed32 (107); - builder.setOptionalFixed64 (108); - builder.setOptionalSfixed32(109); - builder.setOptionalSfixed64(110); - builder.setOptionalFloat (111); - builder.setOptionalDouble (112); - builder.setOptionalBool (true); - builder.setOptionalString ("115"); - builder.setOptionalBytes (toBytes("116")); - - builder.setOptionalGroup( - TestAllTypesLite.OptionalGroup.newBuilder().setA(117).build()); - builder.setOptionalNestedMessage( - TestAllTypesLite.NestedMessage.newBuilder().setBb(118).build()); - builder.setOptionalForeignMessage( - ForeignMessageLite.newBuilder().setC(119).build()); - builder.setOptionalImportMessage( - ImportMessageLite.newBuilder().setD(120).build()); - builder.setOptionalPublicImportMessage( - PublicImportMessageLite.newBuilder().setE(126).build()); - builder.setOptionalLazyMessage( - TestAllTypesLite.NestedMessage.newBuilder().setBb(127).build()); - - builder.setOptionalNestedEnum (TestAllTypesLite.NestedEnum.BAZ); - builder.setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAZ); - builder.setOptionalImportEnum (ImportEnumLite.IMPORT_LITE_BAZ); - - builder.setOptionalStringPiece("124"); - builder.setOptionalCord("125"); - - // ----------------------------------------------------------------- - - builder.addRepeatedInt32 (201); - builder.addRepeatedInt64 (202); - builder.addRepeatedUint32 (203); - builder.addRepeatedUint64 (204); - builder.addRepeatedSint32 (205); - builder.addRepeatedSint64 (206); - builder.addRepeatedFixed32 (207); - builder.addRepeatedFixed64 (208); - builder.addRepeatedSfixed32(209); - builder.addRepeatedSfixed64(210); - builder.addRepeatedFloat (211); - builder.addRepeatedDouble (212); - builder.addRepeatedBool (true); - builder.addRepeatedString ("215"); - builder.addRepeatedBytes (toBytes("216")); - - builder.addRepeatedGroup( - TestAllTypesLite.RepeatedGroup.newBuilder().setA(217).build()); - builder.addRepeatedNestedMessage( - TestAllTypesLite.NestedMessage.newBuilder().setBb(218).build()); - builder.addRepeatedForeignMessage( - ForeignMessageLite.newBuilder().setC(219).build()); - builder.addRepeatedImportMessage( - ImportMessageLite.newBuilder().setD(220).build()); - builder.addRepeatedLazyMessage( - TestAllTypesLite.NestedMessage.newBuilder().setBb(227).build()); - - builder.addRepeatedNestedEnum (TestAllTypesLite.NestedEnum.BAR); - builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR); - builder.addRepeatedImportEnum (ImportEnumLite.IMPORT_LITE_BAR); - - builder.addRepeatedStringPiece("224"); - builder.addRepeatedCord("225"); - - // Add a second one of each field. - builder.addRepeatedInt32 (301); - builder.addRepeatedInt64 (302); - builder.addRepeatedUint32 (303); - builder.addRepeatedUint64 (304); - builder.addRepeatedSint32 (305); - builder.addRepeatedSint64 (306); - builder.addRepeatedFixed32 (307); - builder.addRepeatedFixed64 (308); - builder.addRepeatedSfixed32(309); - builder.addRepeatedSfixed64(310); - builder.addRepeatedFloat (311); - builder.addRepeatedDouble (312); - builder.addRepeatedBool (false); - builder.addRepeatedString ("315"); - builder.addRepeatedBytes (toBytes("316")); - - builder.addRepeatedGroup( - TestAllTypesLite.RepeatedGroup.newBuilder().setA(317).build()); - builder.addRepeatedNestedMessage( - TestAllTypesLite.NestedMessage.newBuilder().setBb(318).build()); - builder.addRepeatedForeignMessage( - ForeignMessageLite.newBuilder().setC(319).build()); - builder.addRepeatedImportMessage( - ImportMessageLite.newBuilder().setD(320).build()); - builder.addRepeatedLazyMessage( - TestAllTypesLite.NestedMessage.newBuilder().setBb(327).build()); - - builder.addRepeatedNestedEnum (TestAllTypesLite.NestedEnum.BAZ); - builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAZ); - builder.addRepeatedImportEnum (ImportEnumLite.IMPORT_LITE_BAZ); - - builder.addRepeatedStringPiece("324"); - builder.addRepeatedCord("325"); - - // ----------------------------------------------------------------- - - builder.setDefaultInt32 (401); - builder.setDefaultInt64 (402); - builder.setDefaultUint32 (403); - builder.setDefaultUint64 (404); - builder.setDefaultSint32 (405); - builder.setDefaultSint64 (406); - builder.setDefaultFixed32 (407); - builder.setDefaultFixed64 (408); - builder.setDefaultSfixed32(409); - builder.setDefaultSfixed64(410); - builder.setDefaultFloat (411); - builder.setDefaultDouble (412); - builder.setDefaultBool (false); - builder.setDefaultString ("415"); - builder.setDefaultBytes (toBytes("416")); - - builder.setDefaultNestedEnum (TestAllTypesLite.NestedEnum.FOO); - builder.setDefaultForeignEnum(ForeignEnumLite.FOREIGN_LITE_FOO); - builder.setDefaultImportEnum (ImportEnumLite.IMPORT_LITE_FOO); - - builder.setDefaultStringPiece("424"); - builder.setDefaultCord("425"); - - builder.setOneofUint32(601); - builder.setOneofNestedMessage( - TestAllTypesLite.NestedMessage.newBuilder().setBb(602).build()); - builder.setOneofString("603"); - builder.setOneofBytes(toBytes("604")); - } - /** * Set every field of {@code message} to the values expected by * {@code assertAllFieldsSet()}. @@ -1383,23 +1196,13 @@ public final class TestUtil { return registry.getUnmodifiable(); } - public static ExtensionRegistryLite getExtensionRegistryLite() { - ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance(); - registerAllExtensionsLite(registry); - return registry.getUnmodifiable(); - } - /** * Register all of {@code TestAllExtensions}'s extensions with the * given {@link ExtensionRegistry}. */ public static void registerAllExtensions(ExtensionRegistry registry) { UnittestProto.registerAllExtensions(registry); - registerAllExtensionsLite(registry); - } - - public static void registerAllExtensionsLite(ExtensionRegistryLite registry) { - UnittestLite.registerAllExtensions(registry); + TestUtilLite.registerAllExtensionsLite(registry); } /** @@ -2192,195 +1995,6 @@ public final class TestUtil { // =================================================================== // Lite extensions - /** - * Set every field of {@code message} to the values expected by - * {@code assertAllExtensionsSet()}. - */ - public static void setAllExtensions(TestAllExtensionsLite.Builder message) { - message.setExtension(optionalInt32ExtensionLite , 101); - message.setExtension(optionalInt64ExtensionLite , 102L); - message.setExtension(optionalUint32ExtensionLite , 103); - message.setExtension(optionalUint64ExtensionLite , 104L); - message.setExtension(optionalSint32ExtensionLite , 105); - message.setExtension(optionalSint64ExtensionLite , 106L); - message.setExtension(optionalFixed32ExtensionLite , 107); - message.setExtension(optionalFixed64ExtensionLite , 108L); - message.setExtension(optionalSfixed32ExtensionLite, 109); - message.setExtension(optionalSfixed64ExtensionLite, 110L); - message.setExtension(optionalFloatExtensionLite , 111F); - message.setExtension(optionalDoubleExtensionLite , 112D); - message.setExtension(optionalBoolExtensionLite , true); - message.setExtension(optionalStringExtensionLite , "115"); - message.setExtension(optionalBytesExtensionLite , toBytes("116")); - - message.setExtension(optionalGroupExtensionLite, - OptionalGroup_extension_lite.newBuilder().setA(117).build()); - message.setExtension(optionalNestedMessageExtensionLite, - TestAllTypesLite.NestedMessage.newBuilder().setBb(118).build()); - message.setExtension(optionalForeignMessageExtensionLite, - ForeignMessageLite.newBuilder().setC(119).build()); - message.setExtension(optionalImportMessageExtensionLite, - ImportMessageLite.newBuilder().setD(120).build()); - message.setExtension(optionalPublicImportMessageExtensionLite, - PublicImportMessageLite.newBuilder().setE(126).build()); - message.setExtension(optionalLazyMessageExtensionLite, - TestAllTypesLite.NestedMessage.newBuilder().setBb(127).build()); - - message.setExtension(optionalNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAZ); - message.setExtension(optionalForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAZ); - message.setExtension(optionalImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_BAZ); - - message.setExtension(optionalStringPieceExtensionLite, "124"); - message.setExtension(optionalCordExtensionLite, "125"); - - // ----------------------------------------------------------------- - - message.addExtension(repeatedInt32ExtensionLite , 201); - message.addExtension(repeatedInt64ExtensionLite , 202L); - message.addExtension(repeatedUint32ExtensionLite , 203); - message.addExtension(repeatedUint64ExtensionLite , 204L); - message.addExtension(repeatedSint32ExtensionLite , 205); - message.addExtension(repeatedSint64ExtensionLite , 206L); - message.addExtension(repeatedFixed32ExtensionLite , 207); - message.addExtension(repeatedFixed64ExtensionLite , 208L); - message.addExtension(repeatedSfixed32ExtensionLite, 209); - message.addExtension(repeatedSfixed64ExtensionLite, 210L); - message.addExtension(repeatedFloatExtensionLite , 211F); - message.addExtension(repeatedDoubleExtensionLite , 212D); - message.addExtension(repeatedBoolExtensionLite , true); - message.addExtension(repeatedStringExtensionLite , "215"); - message.addExtension(repeatedBytesExtensionLite , toBytes("216")); - - message.addExtension(repeatedGroupExtensionLite, - RepeatedGroup_extension_lite.newBuilder().setA(217).build()); - message.addExtension(repeatedNestedMessageExtensionLite, - TestAllTypesLite.NestedMessage.newBuilder().setBb(218).build()); - message.addExtension(repeatedForeignMessageExtensionLite, - ForeignMessageLite.newBuilder().setC(219).build()); - message.addExtension(repeatedImportMessageExtensionLite, - ImportMessageLite.newBuilder().setD(220).build()); - message.addExtension(repeatedLazyMessageExtensionLite, - TestAllTypesLite.NestedMessage.newBuilder().setBb(227).build()); - - message.addExtension(repeatedNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAR); - message.addExtension(repeatedForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAR); - message.addExtension(repeatedImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_BAR); - - message.addExtension(repeatedStringPieceExtensionLite, "224"); - message.addExtension(repeatedCordExtensionLite, "225"); - - // Add a second one of each field. - message.addExtension(repeatedInt32ExtensionLite , 301); - message.addExtension(repeatedInt64ExtensionLite , 302L); - message.addExtension(repeatedUint32ExtensionLite , 303); - message.addExtension(repeatedUint64ExtensionLite , 304L); - message.addExtension(repeatedSint32ExtensionLite , 305); - message.addExtension(repeatedSint64ExtensionLite , 306L); - message.addExtension(repeatedFixed32ExtensionLite , 307); - message.addExtension(repeatedFixed64ExtensionLite , 308L); - message.addExtension(repeatedSfixed32ExtensionLite, 309); - message.addExtension(repeatedSfixed64ExtensionLite, 310L); - message.addExtension(repeatedFloatExtensionLite , 311F); - message.addExtension(repeatedDoubleExtensionLite , 312D); - message.addExtension(repeatedBoolExtensionLite , false); - message.addExtension(repeatedStringExtensionLite , "315"); - message.addExtension(repeatedBytesExtensionLite , toBytes("316")); - - message.addExtension(repeatedGroupExtensionLite, - RepeatedGroup_extension_lite.newBuilder().setA(317).build()); - message.addExtension(repeatedNestedMessageExtensionLite, - TestAllTypesLite.NestedMessage.newBuilder().setBb(318).build()); - message.addExtension(repeatedForeignMessageExtensionLite, - ForeignMessageLite.newBuilder().setC(319).build()); - message.addExtension(repeatedImportMessageExtensionLite, - ImportMessageLite.newBuilder().setD(320).build()); - message.addExtension(repeatedLazyMessageExtensionLite, - TestAllTypesLite.NestedMessage.newBuilder().setBb(327).build()); - - message.addExtension(repeatedNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAZ); - message.addExtension(repeatedForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAZ); - message.addExtension(repeatedImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_BAZ); - - message.addExtension(repeatedStringPieceExtensionLite, "324"); - message.addExtension(repeatedCordExtensionLite, "325"); - - // ----------------------------------------------------------------- - - message.setExtension(defaultInt32ExtensionLite , 401); - message.setExtension(defaultInt64ExtensionLite , 402L); - message.setExtension(defaultUint32ExtensionLite , 403); - message.setExtension(defaultUint64ExtensionLite , 404L); - message.setExtension(defaultSint32ExtensionLite , 405); - message.setExtension(defaultSint64ExtensionLite , 406L); - message.setExtension(defaultFixed32ExtensionLite , 407); - message.setExtension(defaultFixed64ExtensionLite , 408L); - message.setExtension(defaultSfixed32ExtensionLite, 409); - message.setExtension(defaultSfixed64ExtensionLite, 410L); - message.setExtension(defaultFloatExtensionLite , 411F); - message.setExtension(defaultDoubleExtensionLite , 412D); - message.setExtension(defaultBoolExtensionLite , false); - message.setExtension(defaultStringExtensionLite , "415"); - message.setExtension(defaultBytesExtensionLite , toBytes("416")); - - message.setExtension(defaultNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.FOO); - message.setExtension(defaultForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_FOO); - message.setExtension(defaultImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_FOO); - - message.setExtension(defaultStringPieceExtensionLite, "424"); - message.setExtension(defaultCordExtensionLite, "425"); - - message.setExtension(oneofUint32ExtensionLite, 601); - message.setExtension(oneofNestedMessageExtensionLite, - TestAllTypesLite.NestedMessage.newBuilder().setBb(602).build()); - message.setExtension(oneofStringExtensionLite, "603"); - message.setExtension(oneofBytesExtensionLite, toBytes("604")); - } - - // ------------------------------------------------------------------- - - /** - * Modify the repeated extensions of {@code message} to contain the values - * expected by {@code assertRepeatedExtensionsModified()}. - */ - public static void modifyRepeatedExtensions( - TestAllExtensionsLite.Builder message) { - message.setExtension(repeatedInt32ExtensionLite , 1, 501); - message.setExtension(repeatedInt64ExtensionLite , 1, 502L); - message.setExtension(repeatedUint32ExtensionLite , 1, 503); - message.setExtension(repeatedUint64ExtensionLite , 1, 504L); - message.setExtension(repeatedSint32ExtensionLite , 1, 505); - message.setExtension(repeatedSint64ExtensionLite , 1, 506L); - message.setExtension(repeatedFixed32ExtensionLite , 1, 507); - message.setExtension(repeatedFixed64ExtensionLite , 1, 508L); - message.setExtension(repeatedSfixed32ExtensionLite, 1, 509); - message.setExtension(repeatedSfixed64ExtensionLite, 1, 510L); - message.setExtension(repeatedFloatExtensionLite , 1, 511F); - message.setExtension(repeatedDoubleExtensionLite , 1, 512D); - message.setExtension(repeatedBoolExtensionLite , 1, true); - message.setExtension(repeatedStringExtensionLite , 1, "515"); - message.setExtension(repeatedBytesExtensionLite , 1, toBytes("516")); - - message.setExtension(repeatedGroupExtensionLite, 1, - RepeatedGroup_extension_lite.newBuilder().setA(517).build()); - message.setExtension(repeatedNestedMessageExtensionLite, 1, - TestAllTypesLite.NestedMessage.newBuilder().setBb(518).build()); - message.setExtension(repeatedForeignMessageExtensionLite, 1, - ForeignMessageLite.newBuilder().setC(519).build()); - message.setExtension(repeatedImportMessageExtensionLite, 1, - ImportMessageLite.newBuilder().setD(520).build()); - message.setExtension(repeatedLazyMessageExtensionLite, 1, - TestAllTypesLite.NestedMessage.newBuilder().setBb(527).build()); - - message.setExtension(repeatedNestedEnumExtensionLite , 1, TestAllTypesLite.NestedEnum.FOO); - message.setExtension(repeatedForeignEnumExtensionLite, 1, ForeignEnumLite.FOREIGN_LITE_FOO); - message.setExtension(repeatedImportEnumExtensionLite , 1, ImportEnumLite.IMPORT_LITE_FOO); - - message.setExtension(repeatedStringPieceExtensionLite, 1, "524"); - message.setExtension(repeatedCordExtensionLite, 1, "525"); - } - - // ------------------------------------------------------------------- - /** * Assert (using {@code junit.framework.Assert}} that all extensions of * {@code message} are set to the values assigned by {@code setAllExtensions}. @@ -2880,38 +2494,6 @@ public final class TestUtil { assertEqualsExactType("525", message.getExtension(repeatedCordExtensionLite, 1)); } - public static void setPackedExtensions(TestPackedExtensionsLite.Builder message) { - message.addExtension(packedInt32ExtensionLite , 601); - message.addExtension(packedInt64ExtensionLite , 602L); - message.addExtension(packedUint32ExtensionLite , 603); - message.addExtension(packedUint64ExtensionLite , 604L); - message.addExtension(packedSint32ExtensionLite , 605); - message.addExtension(packedSint64ExtensionLite , 606L); - message.addExtension(packedFixed32ExtensionLite , 607); - message.addExtension(packedFixed64ExtensionLite , 608L); - message.addExtension(packedSfixed32ExtensionLite, 609); - message.addExtension(packedSfixed64ExtensionLite, 610L); - message.addExtension(packedFloatExtensionLite , 611F); - message.addExtension(packedDoubleExtensionLite , 612D); - message.addExtension(packedBoolExtensionLite , true); - message.addExtension(packedEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAR); - // Add a second one of each field. - message.addExtension(packedInt32ExtensionLite , 701); - message.addExtension(packedInt64ExtensionLite , 702L); - message.addExtension(packedUint32ExtensionLite , 703); - message.addExtension(packedUint64ExtensionLite , 704L); - message.addExtension(packedSint32ExtensionLite , 705); - message.addExtension(packedSint64ExtensionLite , 706L); - message.addExtension(packedFixed32ExtensionLite , 707); - message.addExtension(packedFixed64ExtensionLite , 708L); - message.addExtension(packedSfixed32ExtensionLite, 709); - message.addExtension(packedSfixed64ExtensionLite, 710L); - message.addExtension(packedFloatExtensionLite , 711F); - message.addExtension(packedDoubleExtensionLite , 712D); - message.addExtension(packedBoolExtensionLite , false); - message.addExtension(packedEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAZ); - } - public static void assertPackedExtensionsSet(TestPackedExtensionsLite message) { Assert.assertEquals(2, message.getExtensionCount(packedInt32ExtensionLite )); Assert.assertEquals(2, message.getExtensionCount(packedInt64ExtensionLite )); @@ -4263,7 +3845,7 @@ public final class TestUtil { private int invalidations; - //@Override (Java 1.6 override semantics, but we must support 1.5) + @Override public void markDirty() { invalidations++; } diff --git a/java/core/src/test/java/com/google/protobuf/TestUtilLite.java b/java/core/src/test/java/com/google/protobuf/TestUtilLite.java new file mode 100644 index 000000000..8f33fa14a --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/TestUtilLite.java @@ -0,0 +1,559 @@ +// 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. + +package com.google.protobuf; + +import static com.google.protobuf.UnittestLite.OptionalGroup_extension_lite; +import static com.google.protobuf.UnittestLite.RepeatedGroup_extension_lite; +import static com.google.protobuf.UnittestLite.defaultBoolExtensionLite; +import static com.google.protobuf.UnittestLite.defaultBytesExtensionLite; +import static com.google.protobuf.UnittestLite.defaultCordExtensionLite; +import static com.google.protobuf.UnittestLite.defaultDoubleExtensionLite; +import static com.google.protobuf.UnittestLite.defaultFixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultFixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultFloatExtensionLite; +import static com.google.protobuf.UnittestLite.defaultForeignEnumExtensionLite; +import static com.google.protobuf.UnittestLite.defaultImportEnumExtensionLite; +import static com.google.protobuf.UnittestLite.defaultInt32ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultInt64ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultNestedEnumExtensionLite; +import static com.google.protobuf.UnittestLite.defaultSfixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultSfixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultSint32ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultSint64ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultStringExtensionLite; +import static com.google.protobuf.UnittestLite.defaultStringPieceExtensionLite; +import static com.google.protobuf.UnittestLite.defaultUint32ExtensionLite; +import static com.google.protobuf.UnittestLite.defaultUint64ExtensionLite; +import static com.google.protobuf.UnittestLite.oneofBytesExtensionLite; +import static com.google.protobuf.UnittestLite.oneofNestedMessageExtensionLite; +import static com.google.protobuf.UnittestLite.oneofStringExtensionLite; +import static com.google.protobuf.UnittestLite.oneofUint32ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalBoolExtensionLite; +import static com.google.protobuf.UnittestLite.optionalBytesExtensionLite; +import static com.google.protobuf.UnittestLite.optionalCordExtensionLite; +import static com.google.protobuf.UnittestLite.optionalDoubleExtensionLite; +import static com.google.protobuf.UnittestLite.optionalFixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalFixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalFloatExtensionLite; +import static com.google.protobuf.UnittestLite.optionalForeignEnumExtensionLite; +import static com.google.protobuf.UnittestLite.optionalForeignMessageExtensionLite; +import static com.google.protobuf.UnittestLite.optionalGroupExtensionLite; +import static com.google.protobuf.UnittestLite.optionalImportEnumExtensionLite; +import static com.google.protobuf.UnittestLite.optionalImportMessageExtensionLite; +import static com.google.protobuf.UnittestLite.optionalInt32ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalInt64ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalLazyMessageExtensionLite; +import static com.google.protobuf.UnittestLite.optionalNestedEnumExtensionLite; +import static com.google.protobuf.UnittestLite.optionalNestedMessageExtensionLite; +import static com.google.protobuf.UnittestLite.optionalPublicImportMessageExtensionLite; +import static com.google.protobuf.UnittestLite.optionalSfixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalSfixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalSint32ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalSint64ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalStringExtensionLite; +import static com.google.protobuf.UnittestLite.optionalStringPieceExtensionLite; +import static com.google.protobuf.UnittestLite.optionalUint32ExtensionLite; +import static com.google.protobuf.UnittestLite.optionalUint64ExtensionLite; +import static com.google.protobuf.UnittestLite.packedBoolExtensionLite; +import static com.google.protobuf.UnittestLite.packedDoubleExtensionLite; +import static com.google.protobuf.UnittestLite.packedEnumExtensionLite; +import static com.google.protobuf.UnittestLite.packedFixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.packedFixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.packedFloatExtensionLite; +import static com.google.protobuf.UnittestLite.packedInt32ExtensionLite; +import static com.google.protobuf.UnittestLite.packedInt64ExtensionLite; +import static com.google.protobuf.UnittestLite.packedSfixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.packedSfixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.packedSint32ExtensionLite; +import static com.google.protobuf.UnittestLite.packedSint64ExtensionLite; +import static com.google.protobuf.UnittestLite.packedUint32ExtensionLite; +import static com.google.protobuf.UnittestLite.packedUint64ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedBoolExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedBytesExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedCordExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedDoubleExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedFixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedFixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedFloatExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedForeignEnumExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedForeignMessageExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedGroupExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedImportEnumExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedImportMessageExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedInt32ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedInt64ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedLazyMessageExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedNestedEnumExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedNestedMessageExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedSfixed32ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedSfixed64ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedSint32ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedSint64ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedStringExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedStringPieceExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedUint32ExtensionLite; +import static com.google.protobuf.UnittestLite.repeatedUint64ExtensionLite; + +import com.google.protobuf.UnittestImportLite.ImportEnumLite; +import com.google.protobuf.UnittestImportLite.ImportMessageLite; +import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite; +import com.google.protobuf.UnittestLite.ForeignEnumLite; +import com.google.protobuf.UnittestLite.ForeignMessageLite; +import com.google.protobuf.UnittestLite.TestAllExtensionsLite; +import com.google.protobuf.UnittestLite.TestAllTypesLite; +import com.google.protobuf.UnittestLite.TestPackedExtensionsLite; + +/** + * Contains methods for setting fields of {@code TestAllTypesLite}, {@code TestAllExtensionsLite}, + * and {@code TestPackedExtensionsLite}. This is analogous to the functionality in TestUtil.java but + * does not depend on the presence of any non-lite protos. + * + *

This code is not to be used outside of {@code com.google.protobuf} and + * subpackages. + */ +public final class TestUtilLite { + private TestUtilLite() {} + + /** Helper to convert a String to ByteString. */ + static ByteString toBytes(String str) { + return ByteString.copyFrom(str.getBytes(Internal.UTF_8)); + } + + /** + * Get a {@code TestAllTypesLite.Builder} with all fields set as they would be by + * {@link #setAllFields(TestAllTypesLite.Builder)}. + */ + public static TestAllTypesLite.Builder getAllLiteSetBuilder() { + TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder(); + setAllFields(builder); + return builder; + } + + /** + * Get a {@code TestAllExtensionsLite} with all fields set as they would be by + * {@link #setAllExtensions(TestAllExtensionsLite.Builder)}. + */ + public static TestAllExtensionsLite getAllLiteExtensionsSet() { + TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder(); + setAllExtensions(builder); + return builder.build(); + } + + public static TestPackedExtensionsLite getLitePackedExtensionsSet() { + TestPackedExtensionsLite.Builder builder = TestPackedExtensionsLite.newBuilder(); + setPackedExtensions(builder); + return builder.build(); + } + + /** + * Set every field of {@code builder} to the values expected by + * {@code assertAllFieldsSet()}. + */ + public static void setAllFields(TestAllTypesLite.Builder builder) { + builder.setOptionalInt32 (101); + builder.setOptionalInt64 (102); + builder.setOptionalUint32 (103); + builder.setOptionalUint64 (104); + builder.setOptionalSint32 (105); + builder.setOptionalSint64 (106); + builder.setOptionalFixed32 (107); + builder.setOptionalFixed64 (108); + builder.setOptionalSfixed32(109); + builder.setOptionalSfixed64(110); + builder.setOptionalFloat (111); + builder.setOptionalDouble (112); + builder.setOptionalBool (true); + builder.setOptionalString ("115"); + builder.setOptionalBytes (toBytes("116")); + + builder.setOptionalGroup( + TestAllTypesLite.OptionalGroup.newBuilder().setA(117).build()); + builder.setOptionalNestedMessage( + TestAllTypesLite.NestedMessage.newBuilder().setBb(118).build()); + builder.setOptionalForeignMessage( + ForeignMessageLite.newBuilder().setC(119).build()); + builder.setOptionalImportMessage( + ImportMessageLite.newBuilder().setD(120).build()); + builder.setOptionalPublicImportMessage( + PublicImportMessageLite.newBuilder().setE(126).build()); + builder.setOptionalLazyMessage( + TestAllTypesLite.NestedMessage.newBuilder().setBb(127).build()); + + builder.setOptionalNestedEnum (TestAllTypesLite.NestedEnum.BAZ); + builder.setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAZ); + builder.setOptionalImportEnum (ImportEnumLite.IMPORT_LITE_BAZ); + + builder.setOptionalStringPiece("124"); + builder.setOptionalCord("125"); + + // ----------------------------------------------------------------- + + builder.addRepeatedInt32 (201); + builder.addRepeatedInt64 (202); + builder.addRepeatedUint32 (203); + builder.addRepeatedUint64 (204); + builder.addRepeatedSint32 (205); + builder.addRepeatedSint64 (206); + builder.addRepeatedFixed32 (207); + builder.addRepeatedFixed64 (208); + builder.addRepeatedSfixed32(209); + builder.addRepeatedSfixed64(210); + builder.addRepeatedFloat (211); + builder.addRepeatedDouble (212); + builder.addRepeatedBool (true); + builder.addRepeatedString ("215"); + builder.addRepeatedBytes (toBytes("216")); + + builder.addRepeatedGroup( + TestAllTypesLite.RepeatedGroup.newBuilder().setA(217).build()); + builder.addRepeatedNestedMessage( + TestAllTypesLite.NestedMessage.newBuilder().setBb(218).build()); + builder.addRepeatedForeignMessage( + ForeignMessageLite.newBuilder().setC(219).build()); + builder.addRepeatedImportMessage( + ImportMessageLite.newBuilder().setD(220).build()); + builder.addRepeatedLazyMessage( + TestAllTypesLite.NestedMessage.newBuilder().setBb(227).build()); + + builder.addRepeatedNestedEnum (TestAllTypesLite.NestedEnum.BAR); + builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR); + builder.addRepeatedImportEnum (ImportEnumLite.IMPORT_LITE_BAR); + + builder.addRepeatedStringPiece("224"); + builder.addRepeatedCord("225"); + + // Add a second one of each field. + builder.addRepeatedInt32 (301); + builder.addRepeatedInt64 (302); + builder.addRepeatedUint32 (303); + builder.addRepeatedUint64 (304); + builder.addRepeatedSint32 (305); + builder.addRepeatedSint64 (306); + builder.addRepeatedFixed32 (307); + builder.addRepeatedFixed64 (308); + builder.addRepeatedSfixed32(309); + builder.addRepeatedSfixed64(310); + builder.addRepeatedFloat (311); + builder.addRepeatedDouble (312); + builder.addRepeatedBool (false); + builder.addRepeatedString ("315"); + builder.addRepeatedBytes (toBytes("316")); + + builder.addRepeatedGroup( + TestAllTypesLite.RepeatedGroup.newBuilder().setA(317).build()); + builder.addRepeatedNestedMessage( + TestAllTypesLite.NestedMessage.newBuilder().setBb(318).build()); + builder.addRepeatedForeignMessage( + ForeignMessageLite.newBuilder().setC(319).build()); + builder.addRepeatedImportMessage( + ImportMessageLite.newBuilder().setD(320).build()); + builder.addRepeatedLazyMessage( + TestAllTypesLite.NestedMessage.newBuilder().setBb(327).build()); + + builder.addRepeatedNestedEnum (TestAllTypesLite.NestedEnum.BAZ); + builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAZ); + builder.addRepeatedImportEnum (ImportEnumLite.IMPORT_LITE_BAZ); + + builder.addRepeatedStringPiece("324"); + builder.addRepeatedCord("325"); + + // ----------------------------------------------------------------- + + builder.setDefaultInt32 (401); + builder.setDefaultInt64 (402); + builder.setDefaultUint32 (403); + builder.setDefaultUint64 (404); + builder.setDefaultSint32 (405); + builder.setDefaultSint64 (406); + builder.setDefaultFixed32 (407); + builder.setDefaultFixed64 (408); + builder.setDefaultSfixed32(409); + builder.setDefaultSfixed64(410); + builder.setDefaultFloat (411); + builder.setDefaultDouble (412); + builder.setDefaultBool (false); + builder.setDefaultString ("415"); + builder.setDefaultBytes (toBytes("416")); + + builder.setDefaultNestedEnum (TestAllTypesLite.NestedEnum.FOO); + builder.setDefaultForeignEnum(ForeignEnumLite.FOREIGN_LITE_FOO); + builder.setDefaultImportEnum (ImportEnumLite.IMPORT_LITE_FOO); + + builder.setDefaultStringPiece("424"); + builder.setDefaultCord("425"); + + builder.setOneofUint32(601); + builder.setOneofNestedMessage( + TestAllTypesLite.NestedMessage.newBuilder().setBb(602).build()); + builder.setOneofString("603"); + builder.setOneofBytes(toBytes("604")); + } + + /** + * Get an unmodifiable {@link ExtensionRegistryLite} containing all the + * extensions of {@code TestAllExtensionsLite}. + */ + public static ExtensionRegistryLite getExtensionRegistryLite() { + ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance(); + registerAllExtensionsLite(registry); + return registry.getUnmodifiable(); + } + + /** + * Register all of {@code TestAllExtensionsLite}'s extensions with the + * given {@link ExtensionRegistryLite}. + */ + public static void registerAllExtensionsLite(ExtensionRegistryLite registry) { + UnittestLite.registerAllExtensions(registry); + } + + // =================================================================== + // Lite extensions + + /** + * Set every field of {@code message} to the values expected by + * {@code assertAllExtensionsSet()}. + */ + public static void setAllExtensions(TestAllExtensionsLite.Builder message) { + message.setExtension(optionalInt32ExtensionLite , 101); + message.setExtension(optionalInt64ExtensionLite , 102L); + message.setExtension(optionalUint32ExtensionLite , 103); + message.setExtension(optionalUint64ExtensionLite , 104L); + message.setExtension(optionalSint32ExtensionLite , 105); + message.setExtension(optionalSint64ExtensionLite , 106L); + message.setExtension(optionalFixed32ExtensionLite , 107); + message.setExtension(optionalFixed64ExtensionLite , 108L); + message.setExtension(optionalSfixed32ExtensionLite, 109); + message.setExtension(optionalSfixed64ExtensionLite, 110L); + message.setExtension(optionalFloatExtensionLite , 111F); + message.setExtension(optionalDoubleExtensionLite , 112D); + message.setExtension(optionalBoolExtensionLite , true); + message.setExtension(optionalStringExtensionLite , "115"); + message.setExtension(optionalBytesExtensionLite , toBytes("116")); + + message.setExtension(optionalGroupExtensionLite, + OptionalGroup_extension_lite.newBuilder().setA(117).build()); + message.setExtension(optionalNestedMessageExtensionLite, + TestAllTypesLite.NestedMessage.newBuilder().setBb(118).build()); + message.setExtension(optionalForeignMessageExtensionLite, + ForeignMessageLite.newBuilder().setC(119).build()); + message.setExtension(optionalImportMessageExtensionLite, + ImportMessageLite.newBuilder().setD(120).build()); + message.setExtension(optionalPublicImportMessageExtensionLite, + PublicImportMessageLite.newBuilder().setE(126).build()); + message.setExtension(optionalLazyMessageExtensionLite, + TestAllTypesLite.NestedMessage.newBuilder().setBb(127).build()); + + message.setExtension(optionalNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAZ); + message.setExtension(optionalForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAZ); + message.setExtension(optionalImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_BAZ); + + message.setExtension(optionalStringPieceExtensionLite, "124"); + message.setExtension(optionalCordExtensionLite, "125"); + + // ----------------------------------------------------------------- + + message.addExtension(repeatedInt32ExtensionLite , 201); + message.addExtension(repeatedInt64ExtensionLite , 202L); + message.addExtension(repeatedUint32ExtensionLite , 203); + message.addExtension(repeatedUint64ExtensionLite , 204L); + message.addExtension(repeatedSint32ExtensionLite , 205); + message.addExtension(repeatedSint64ExtensionLite , 206L); + message.addExtension(repeatedFixed32ExtensionLite , 207); + message.addExtension(repeatedFixed64ExtensionLite , 208L); + message.addExtension(repeatedSfixed32ExtensionLite, 209); + message.addExtension(repeatedSfixed64ExtensionLite, 210L); + message.addExtension(repeatedFloatExtensionLite , 211F); + message.addExtension(repeatedDoubleExtensionLite , 212D); + message.addExtension(repeatedBoolExtensionLite , true); + message.addExtension(repeatedStringExtensionLite , "215"); + message.addExtension(repeatedBytesExtensionLite , toBytes("216")); + + message.addExtension(repeatedGroupExtensionLite, + RepeatedGroup_extension_lite.newBuilder().setA(217).build()); + message.addExtension(repeatedNestedMessageExtensionLite, + TestAllTypesLite.NestedMessage.newBuilder().setBb(218).build()); + message.addExtension(repeatedForeignMessageExtensionLite, + ForeignMessageLite.newBuilder().setC(219).build()); + message.addExtension(repeatedImportMessageExtensionLite, + ImportMessageLite.newBuilder().setD(220).build()); + message.addExtension(repeatedLazyMessageExtensionLite, + TestAllTypesLite.NestedMessage.newBuilder().setBb(227).build()); + + message.addExtension(repeatedNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAR); + message.addExtension(repeatedForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAR); + message.addExtension(repeatedImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_BAR); + + message.addExtension(repeatedStringPieceExtensionLite, "224"); + message.addExtension(repeatedCordExtensionLite, "225"); + + // Add a second one of each field. + message.addExtension(repeatedInt32ExtensionLite , 301); + message.addExtension(repeatedInt64ExtensionLite , 302L); + message.addExtension(repeatedUint32ExtensionLite , 303); + message.addExtension(repeatedUint64ExtensionLite , 304L); + message.addExtension(repeatedSint32ExtensionLite , 305); + message.addExtension(repeatedSint64ExtensionLite , 306L); + message.addExtension(repeatedFixed32ExtensionLite , 307); + message.addExtension(repeatedFixed64ExtensionLite , 308L); + message.addExtension(repeatedSfixed32ExtensionLite, 309); + message.addExtension(repeatedSfixed64ExtensionLite, 310L); + message.addExtension(repeatedFloatExtensionLite , 311F); + message.addExtension(repeatedDoubleExtensionLite , 312D); + message.addExtension(repeatedBoolExtensionLite , false); + message.addExtension(repeatedStringExtensionLite , "315"); + message.addExtension(repeatedBytesExtensionLite , toBytes("316")); + + message.addExtension(repeatedGroupExtensionLite, + RepeatedGroup_extension_lite.newBuilder().setA(317).build()); + message.addExtension(repeatedNestedMessageExtensionLite, + TestAllTypesLite.NestedMessage.newBuilder().setBb(318).build()); + message.addExtension(repeatedForeignMessageExtensionLite, + ForeignMessageLite.newBuilder().setC(319).build()); + message.addExtension(repeatedImportMessageExtensionLite, + ImportMessageLite.newBuilder().setD(320).build()); + message.addExtension(repeatedLazyMessageExtensionLite, + TestAllTypesLite.NestedMessage.newBuilder().setBb(327).build()); + + message.addExtension(repeatedNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAZ); + message.addExtension(repeatedForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAZ); + message.addExtension(repeatedImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_BAZ); + + message.addExtension(repeatedStringPieceExtensionLite, "324"); + message.addExtension(repeatedCordExtensionLite, "325"); + + // ----------------------------------------------------------------- + + message.setExtension(defaultInt32ExtensionLite , 401); + message.setExtension(defaultInt64ExtensionLite , 402L); + message.setExtension(defaultUint32ExtensionLite , 403); + message.setExtension(defaultUint64ExtensionLite , 404L); + message.setExtension(defaultSint32ExtensionLite , 405); + message.setExtension(defaultSint64ExtensionLite , 406L); + message.setExtension(defaultFixed32ExtensionLite , 407); + message.setExtension(defaultFixed64ExtensionLite , 408L); + message.setExtension(defaultSfixed32ExtensionLite, 409); + message.setExtension(defaultSfixed64ExtensionLite, 410L); + message.setExtension(defaultFloatExtensionLite , 411F); + message.setExtension(defaultDoubleExtensionLite , 412D); + message.setExtension(defaultBoolExtensionLite , false); + message.setExtension(defaultStringExtensionLite , "415"); + message.setExtension(defaultBytesExtensionLite , toBytes("416")); + + message.setExtension(defaultNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.FOO); + message.setExtension(defaultForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_FOO); + message.setExtension(defaultImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_FOO); + + message.setExtension(defaultStringPieceExtensionLite, "424"); + message.setExtension(defaultCordExtensionLite, "425"); + + message.setExtension(oneofUint32ExtensionLite, 601); + message.setExtension(oneofNestedMessageExtensionLite, + TestAllTypesLite.NestedMessage.newBuilder().setBb(602).build()); + message.setExtension(oneofStringExtensionLite, "603"); + message.setExtension(oneofBytesExtensionLite, toBytes("604")); + } + + // ------------------------------------------------------------------- + + /** + * Modify the repeated extensions of {@code message} to contain the values + * expected by {@code assertRepeatedExtensionsModified()}. + */ + public static void modifyRepeatedExtensions( + TestAllExtensionsLite.Builder message) { + message.setExtension(repeatedInt32ExtensionLite , 1, 501); + message.setExtension(repeatedInt64ExtensionLite , 1, 502L); + message.setExtension(repeatedUint32ExtensionLite , 1, 503); + message.setExtension(repeatedUint64ExtensionLite , 1, 504L); + message.setExtension(repeatedSint32ExtensionLite , 1, 505); + message.setExtension(repeatedSint64ExtensionLite , 1, 506L); + message.setExtension(repeatedFixed32ExtensionLite , 1, 507); + message.setExtension(repeatedFixed64ExtensionLite , 1, 508L); + message.setExtension(repeatedSfixed32ExtensionLite, 1, 509); + message.setExtension(repeatedSfixed64ExtensionLite, 1, 510L); + message.setExtension(repeatedFloatExtensionLite , 1, 511F); + message.setExtension(repeatedDoubleExtensionLite , 1, 512D); + message.setExtension(repeatedBoolExtensionLite , 1, true); + message.setExtension(repeatedStringExtensionLite , 1, "515"); + message.setExtension(repeatedBytesExtensionLite , 1, toBytes("516")); + + message.setExtension(repeatedGroupExtensionLite, 1, + RepeatedGroup_extension_lite.newBuilder().setA(517).build()); + message.setExtension(repeatedNestedMessageExtensionLite, 1, + TestAllTypesLite.NestedMessage.newBuilder().setBb(518).build()); + message.setExtension(repeatedForeignMessageExtensionLite, 1, + ForeignMessageLite.newBuilder().setC(519).build()); + message.setExtension(repeatedImportMessageExtensionLite, 1, + ImportMessageLite.newBuilder().setD(520).build()); + message.setExtension(repeatedLazyMessageExtensionLite, 1, + TestAllTypesLite.NestedMessage.newBuilder().setBb(527).build()); + + message.setExtension(repeatedNestedEnumExtensionLite , 1, TestAllTypesLite.NestedEnum.FOO); + message.setExtension(repeatedForeignEnumExtensionLite, 1, ForeignEnumLite.FOREIGN_LITE_FOO); + message.setExtension(repeatedImportEnumExtensionLite , 1, ImportEnumLite.IMPORT_LITE_FOO); + + message.setExtension(repeatedStringPieceExtensionLite, 1, "524"); + message.setExtension(repeatedCordExtensionLite, 1, "525"); + } + + public static void setPackedExtensions(TestPackedExtensionsLite.Builder message) { + message.addExtension(packedInt32ExtensionLite , 601); + message.addExtension(packedInt64ExtensionLite , 602L); + message.addExtension(packedUint32ExtensionLite , 603); + message.addExtension(packedUint64ExtensionLite , 604L); + message.addExtension(packedSint32ExtensionLite , 605); + message.addExtension(packedSint64ExtensionLite , 606L); + message.addExtension(packedFixed32ExtensionLite , 607); + message.addExtension(packedFixed64ExtensionLite , 608L); + message.addExtension(packedSfixed32ExtensionLite, 609); + message.addExtension(packedSfixed64ExtensionLite, 610L); + message.addExtension(packedFloatExtensionLite , 611F); + message.addExtension(packedDoubleExtensionLite , 612D); + message.addExtension(packedBoolExtensionLite , true); + message.addExtension(packedEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAR); + // Add a second one of each field. + message.addExtension(packedInt32ExtensionLite , 701); + message.addExtension(packedInt64ExtensionLite , 702L); + message.addExtension(packedUint32ExtensionLite , 703); + message.addExtension(packedUint64ExtensionLite , 704L); + message.addExtension(packedSint32ExtensionLite , 705); + message.addExtension(packedSint64ExtensionLite , 706L); + message.addExtension(packedFixed32ExtensionLite , 707); + message.addExtension(packedFixed64ExtensionLite , 708L); + message.addExtension(packedSfixed32ExtensionLite, 709); + message.addExtension(packedSfixed64ExtensionLite, 710L); + message.addExtension(packedFloatExtensionLite , 711F); + message.addExtension(packedDoubleExtensionLite , 712D); + message.addExtension(packedBoolExtensionLite , false); + message.addExtension(packedEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAZ); + } +} diff --git a/java/core/src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java b/java/core/src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java new file mode 100644 index 000000000..e338af211 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java @@ -0,0 +1,182 @@ +// 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. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import protobuf_unittest.UnittestProto.TestAllTypes; + +import junit.framework.TestCase; + +/** + * Test @{link TextFormatParseInfoTree}. + */ +public class TextFormatParseInfoTreeTest extends TestCase { + + private static final Descriptor DESCRIPTOR = TestAllTypes.getDescriptor(); + private static final FieldDescriptor OPTIONAL_INT32 = + DESCRIPTOR.findFieldByName("optional_int32"); + private static final FieldDescriptor OPTIONAL_BOOLEAN = + DESCRIPTOR.findFieldByName("optional_boolean"); + private static final FieldDescriptor REPEATED_INT32 = + DESCRIPTOR.findFieldByName("repeated_int32"); + private static final FieldDescriptor OPTIONAL_NESTED_MESSAGE = + DESCRIPTOR.findFieldByName("optional_nested_message"); + private static final FieldDescriptor REPEATED_NESTED_MESSAGE = + DESCRIPTOR.findFieldByName("repeated_nested_message"); + private static final FieldDescriptor FIELD_BB = + TestAllTypes.NestedMessage.getDescriptor().findFieldByName("bb"); + + private static final TextFormatParseLocation LOC0 = TextFormatParseLocation.create(1, 2); + private static final TextFormatParseLocation LOC1 = TextFormatParseLocation.create(2, 3); + + private TextFormatParseInfoTree.Builder rootBuilder; + + @Override + public void setUp() { + rootBuilder = TextFormatParseInfoTree.builder(); + } + + public void testBuildEmptyParseTree() { + TextFormatParseInfoTree tree = rootBuilder.build(); + assertTrue(tree.getLocations(null).isEmpty()); + } + + public void testGetLocationReturnsSingleLocation() { + rootBuilder.setLocation(OPTIONAL_INT32, LOC0); + TextFormatParseInfoTree root = rootBuilder.build(); + assertEquals(LOC0, root.getLocation(OPTIONAL_INT32, 0)); + assertEquals(1, root.getLocations(OPTIONAL_INT32).size()); + } + + public void testGetLocationsReturnsNoParseLocationsForUnknownField() { + assertTrue(rootBuilder.build().getLocations(OPTIONAL_INT32).isEmpty()); + rootBuilder.setLocation(OPTIONAL_BOOLEAN, LOC0); + TextFormatParseInfoTree root = rootBuilder.build(); + assertTrue(root.getLocations(OPTIONAL_INT32).isEmpty()); + assertEquals(LOC0, root.getLocations(OPTIONAL_BOOLEAN).get(0)); + } + + public void testGetLocationThrowsIllegalArgumentExceptionForUnknownField() { + rootBuilder.setLocation(REPEATED_INT32, LOC0); + TextFormatParseInfoTree root = rootBuilder.build(); + try { + root.getNestedTree(OPTIONAL_INT32, 0); + fail("Did not detect unknown field"); + } catch (IllegalArgumentException expected) { + // pass + } + } + + public void testGetLocationThrowsIllegalArgumentExceptionForInvalidIndex() { + TextFormatParseInfoTree root = rootBuilder.setLocation(OPTIONAL_INT32, LOC0).build(); + try { + root.getLocation(OPTIONAL_INT32, 1); + fail("Invalid index not detected"); + } catch (IllegalArgumentException expected) { + // pass + } + try { + root.getLocation(OPTIONAL_INT32, -1); + fail("Negative index not detected"); + } catch (IllegalArgumentException expected) { + // pass + } + } + + public void testGetLocationsReturnsMultipleLocations() { + rootBuilder.setLocation(REPEATED_INT32, LOC0); + rootBuilder.setLocation(REPEATED_INT32, LOC1); + TextFormatParseInfoTree root = rootBuilder.build(); + assertEquals(LOC0, root.getLocation(REPEATED_INT32, 0)); + assertEquals(LOC1, root.getLocation(REPEATED_INT32, 1)); + assertEquals(2, root.getLocations(REPEATED_INT32).size()); + } + + public void testGetNestedTreeThrowsIllegalArgumentExceptionForUnknownField() { + rootBuilder.setLocation(REPEATED_INT32, LOC0); + TextFormatParseInfoTree root = rootBuilder.build(); + try { + root.getNestedTree(OPTIONAL_NESTED_MESSAGE, 0); + fail("Did not detect unknown field"); + } catch (IllegalArgumentException expected) { + // pass + } + } + + public void testGetNestedTreesReturnsNoParseInfoTreesForUnknownField() { + rootBuilder.setLocation(REPEATED_INT32, LOC0); + TextFormatParseInfoTree root = rootBuilder.build(); + assertTrue(root.getNestedTrees(OPTIONAL_NESTED_MESSAGE).isEmpty()); + } + + public void testGetNestedTreeThrowsIllegalArgumentExceptionForInvalidIndex() { + rootBuilder.setLocation(REPEATED_INT32, LOC0); + rootBuilder.getBuilderForSubMessageField(OPTIONAL_NESTED_MESSAGE); + TextFormatParseInfoTree root = rootBuilder.build(); + try { + root.getNestedTree(OPTIONAL_NESTED_MESSAGE, 1); + fail("Submessage index that is too large not detected"); + } catch (IllegalArgumentException expected) { + // pass + } + try { + rootBuilder.build().getNestedTree(OPTIONAL_NESTED_MESSAGE, -1); + fail("Invalid submessage index (-1) not detected"); + } catch (IllegalArgumentException expected) { + // pass + } + } + + public void testGetNestedTreesReturnsSingleTree() { + rootBuilder.getBuilderForSubMessageField(OPTIONAL_NESTED_MESSAGE); + TextFormatParseInfoTree root = rootBuilder.build(); + assertEquals(1, root.getNestedTrees(OPTIONAL_NESTED_MESSAGE).size()); + TextFormatParseInfoTree subtree = root.getNestedTrees(OPTIONAL_NESTED_MESSAGE).get(0); + assertNotNull(subtree); + } + + public void testGetNestedTreesReturnsMultipleTrees() { + TextFormatParseInfoTree.Builder subtree1Builder = + rootBuilder.getBuilderForSubMessageField(REPEATED_NESTED_MESSAGE); + subtree1Builder.getBuilderForSubMessageField(FIELD_BB); + subtree1Builder.getBuilderForSubMessageField(FIELD_BB); + TextFormatParseInfoTree.Builder subtree2Builder = + rootBuilder.getBuilderForSubMessageField(REPEATED_NESTED_MESSAGE); + subtree2Builder.getBuilderForSubMessageField(FIELD_BB); + TextFormatParseInfoTree root = rootBuilder.build(); + assertEquals(2, root.getNestedTrees(REPEATED_NESTED_MESSAGE).size()); + assertEquals( + 2, root.getNestedTrees(REPEATED_NESTED_MESSAGE).get(0).getNestedTrees(FIELD_BB).size()); + assertEquals( + 1, root.getNestedTrees(REPEATED_NESTED_MESSAGE).get(1).getNestedTrees(FIELD_BB).size()); + } +} diff --git a/java/core/src/test/java/com/google/protobuf/TextFormatParseLocationTest.java b/java/core/src/test/java/com/google/protobuf/TextFormatParseLocationTest.java new file mode 100644 index 000000000..c42bfa6e7 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/TextFormatParseLocationTest.java @@ -0,0 +1,86 @@ +// 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. + +package com.google.protobuf; + +import junit.framework.TestCase; + +/** + * Test @{link TextFormatParseLocation}. + */ +public class TextFormatParseLocationTest extends TestCase { + + public void testCreateEmpty() { + TextFormatParseLocation location = TextFormatParseLocation.create(-1, -1); + assertEquals(TextFormatParseLocation.EMPTY, location); + } + + public void testCreate() { + TextFormatParseLocation location = TextFormatParseLocation.create(2, 1); + assertEquals(2, location.getLine()); + assertEquals(1, location.getColumn()); + } + + public void testCreateThrowsIllegalArgumentExceptionForInvalidIndex() { + try { + TextFormatParseLocation.create(-1, 0); + fail("Should throw IllegalArgumentException if line is less than 0"); + } catch (IllegalArgumentException unused) { + // pass + } + try { + TextFormatParseLocation.create(0, -1); + fail("Should throw, column < 0"); + } catch (IllegalArgumentException unused) { + // pass + } + } + + public void testHashCode() { + TextFormatParseLocation loc0 = TextFormatParseLocation.create(2, 1); + TextFormatParseLocation loc1 = TextFormatParseLocation.create(2, 1); + + assertEquals(loc0.hashCode(), loc1.hashCode()); + assertEquals( + TextFormatParseLocation.EMPTY.hashCode(), TextFormatParseLocation.EMPTY.hashCode()); + } + + public void testEquals() { + TextFormatParseLocation loc0 = TextFormatParseLocation.create(2, 1); + TextFormatParseLocation loc1 = TextFormatParseLocation.create(1, 2); + TextFormatParseLocation loc2 = TextFormatParseLocation.create(2, 2); + TextFormatParseLocation loc3 = TextFormatParseLocation.create(2, 1); + + assertEquals(loc0, loc3); + assertNotSame(loc0, loc1); + assertNotSame(loc0, loc2); + assertNotSame(loc1, loc2); + } +} diff --git a/java/core/src/test/java/com/google/protobuf/TextFormatTest.java b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java index 1df4fad77..63c17cd0f 100644 --- a/java/core/src/test/java/com/google/protobuf/TextFormatTest.java +++ b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java @@ -30,6 +30,7 @@ package com.google.protobuf; +import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.TextFormat.Parser.SingularOverwritePolicy; import protobuf_unittest.UnittestMset.TestMessageSetExtension1; @@ -45,6 +46,7 @@ import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet; import junit.framework.TestCase; import java.io.StringReader; +import java.util.List; /** * Test case for {@link TextFormat}. @@ -568,6 +570,16 @@ public class TextFormatTest extends TestCase { assertEquals(kEscapeTestString, TextFormat.unescapeText(kEscapeTestStringEscaped)); + // Invariant + assertEquals("hello", + TextFormat.escapeBytes(bytes("hello"))); + assertEquals("hello", + TextFormat.escapeText("hello")); + assertEquals(bytes("hello"), + TextFormat.unescapeBytes("hello")); + assertEquals("hello", + TextFormat.unescapeText("hello")); + // Unicode handling. assertEquals("\\341\\210\\264", TextFormat.escapeText("\u1234")); assertEquals("\\341\\210\\264", @@ -763,11 +775,14 @@ public class TextFormatTest extends TestCase { public void testParseBoolean() throws Exception { String goodText = "repeated_bool: t repeated_bool : 0\n" + - "repeated_bool :f repeated_bool:1"; + "repeated_bool :f repeated_bool:1\n" + + "repeated_bool: False repeated_bool: True"; String goodTextCanonical = "repeated_bool: true\n" + "repeated_bool: false\n" + "repeated_bool: false\n" + + "repeated_bool: true\n" + + "repeated_bool: false\n" + "repeated_bool: true\n"; TestAllTypes.Builder builder = TestAllTypes.newBuilder(); TextFormat.merge(goodText, builder); @@ -811,7 +826,6 @@ public class TextFormatTest extends TestCase { private void assertPrintFieldValue(String expect, Object value, String fieldName) throws Exception { - TestAllTypes.Builder builder = TestAllTypes.newBuilder(); StringBuilder sb = new StringBuilder(); TextFormat.printFieldValue( TestAllTypes.getDescriptor().findFieldByName(fieldName), @@ -1018,4 +1032,98 @@ public class TextFormatTest extends TestCase { assertFalse(oneof.hasFooString()); assertTrue(oneof.hasFooInt()); } + + // ======================================================================= + // test location information + + public void testParseInfoTreeBuilding() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + + Descriptor descriptor = TestAllTypes.getDescriptor(); + TextFormatParseInfoTree.Builder treeBuilder = TextFormatParseInfoTree.builder(); + // Set to allow unknown fields + TextFormat.Parser parser = + TextFormat.Parser.newBuilder() + .setParseInfoTreeBuilder(treeBuilder) + .build(); + + final String stringData = + "optional_int32: 1\n" + + "optional_int64: 2\n" + + " optional_double: 2.4\n" + + "repeated_int32: 5\n" + + "repeated_int32: 10\n" + + "optional_nested_message <\n" + + " bb: 78\n" + + ">\n" + + "repeated_nested_message <\n" + + " bb: 79\n" + + ">\n" + + "repeated_nested_message <\n" + + " bb: 80\n" + + ">"; + + parser.merge(stringData, builder); + TextFormatParseInfoTree tree = treeBuilder.build(); + + // Verify that the tree has the correct positions. + assertLocation(tree, descriptor, "optional_int32", 0, 0, 0); + assertLocation(tree, descriptor, "optional_int64", 0, 1, 0); + assertLocation(tree, descriptor, "optional_double", 0, 2, 2); + + assertLocation(tree, descriptor, "repeated_int32", 0, 3, 0); + assertLocation(tree, descriptor, "repeated_int32", 1, 4, 0); + + assertLocation(tree, descriptor, "optional_nested_message", 0, 5, 0); + assertLocation(tree, descriptor, "repeated_nested_message", 0, 8, 0); + assertLocation(tree, descriptor, "repeated_nested_message", 1, 11, 0); + + // Check for fields not set. For an invalid field, the location returned should be -1, -1. + assertLocation(tree, descriptor, "repeated_int64", 0, -1, -1); + assertLocation(tree, descriptor, "repeated_int32", 6, -1, -1); + + // Verify inside the nested message. + FieldDescriptor nestedField = descriptor.findFieldByName("optional_nested_message"); + + TextFormatParseInfoTree nestedTree = tree.getNestedTrees(nestedField).get(0); + assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 6, 2); + + // Verify inside another nested message. + nestedField = descriptor.findFieldByName("repeated_nested_message"); + nestedTree = tree.getNestedTrees(nestedField).get(0); + assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 9, 2); + + nestedTree = tree.getNestedTrees(nestedField).get(1); + assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 12, 2); + + // Verify a NULL tree for an unknown nested field. + try { + tree.getNestedTree(nestedField, 2); + fail("unknown nested field should throw"); + } catch (IllegalArgumentException unused) { + // pass + } + } + + private void assertLocation( + TextFormatParseInfoTree tree, + final Descriptor descriptor, + final String fieldName, + int index, + int line, + int column) { + List locs = tree.getLocations(descriptor.findFieldByName(fieldName)); + if (index < locs.size()) { + TextFormatParseLocation location = locs.get(index); + TextFormatParseLocation expected = TextFormatParseLocation.create(line, column); + assertEquals(expected, location); + } else if (line != -1 && column != -1) { + fail( + String.format( + "Tree/descriptor/fieldname did not contain index %d, line %d column %d expected", + index, + line, + column)); + } + } } diff --git a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java index dc987379f..573cd6654 100644 --- a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java +++ b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java @@ -47,46 +47,6 @@ import java.io.IOException; * @author dweis@google.com (Daniel Weis) */ public class UnknownFieldSetLiteTest extends TestCase { - - public void testNoDataIsDefaultInstance() { - assertSame( - UnknownFieldSetLite.getDefaultInstance(), - UnknownFieldSetLite.newBuilder() - .build()); - } - - public void testBuilderReuse() throws IOException { - UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder(); - builder.mergeVarintField(10, 2); - builder.build(); - - try { - builder.build(); - fail(); - } catch (UnsupportedOperationException e) { - // Expected. - } - - try { - builder.mergeFieldFrom(0, CodedInputStream.newInstance(new byte[0])); - fail(); - } catch (UnsupportedOperationException e) { - // Expected. - } - - try { - builder.mergeVarintField(5, 1); - fail(); - } catch (UnsupportedOperationException e) { - // Expected. - } - } - - public void testBuilderReuse_empty() { - UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder(); - builder.build(); - builder.build(); - } public void testDefaultInstance() { UnknownFieldSetLite unknownFields = UnknownFieldSetLite.getDefaultInstance(); diff --git a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java index 8c9dcafee..32380f700 100644 --- a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java +++ b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java @@ -50,6 +50,7 @@ import java.util.Map; * @author kenton@google.com (Kenton Varda) */ public class UnknownFieldSetTest extends TestCase { + @Override public void setUp() throws Exception { descriptor = TestAllTypes.getDescriptor(); allFields = TestUtil.getAllSet(); diff --git a/java/core/src/test/java/com/google/protobuf/WireFormatTest.java b/java/core/src/test/java/com/google/protobuf/WireFormatTest.java index 0175005d2..e66b371cd 100644 --- a/java/core/src/test/java/com/google/protobuf/WireFormatTest.java +++ b/java/core/src/test/java/com/google/protobuf/WireFormatTest.java @@ -30,12 +30,11 @@ package com.google.protobuf; -import junit.framework.TestCase; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.util.List; - +import com.google.protobuf.UnittestLite.TestAllExtensionsLite; +import com.google.protobuf.UnittestLite.TestPackedExtensionsLite; +import protobuf_unittest.UnittestMset.RawMessageSet; +import protobuf_unittest.UnittestMset.TestMessageSetExtension1; +import protobuf_unittest.UnittestMset.TestMessageSetExtension2; import protobuf_unittest.UnittestProto; import protobuf_unittest.UnittestProto.TestAllExtensions; import protobuf_unittest.UnittestProto.TestAllTypes; @@ -44,12 +43,13 @@ import protobuf_unittest.UnittestProto.TestOneof2; import protobuf_unittest.UnittestProto.TestOneofBackwardsCompatible; import protobuf_unittest.UnittestProto.TestPackedExtensions; import protobuf_unittest.UnittestProto.TestPackedTypes; -import protobuf_unittest.UnittestMset.RawMessageSet; -import protobuf_unittest.UnittestMset.TestMessageSetExtension1; -import protobuf_unittest.UnittestMset.TestMessageSetExtension2; import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet; -import com.google.protobuf.UnittestLite.TestAllExtensionsLite; -import com.google.protobuf.UnittestLite.TestPackedExtensionsLite; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.List; /** * Tests related to parsing and serialization. @@ -132,7 +132,7 @@ public class WireFormatTest extends TestCase { // so if we serialize a TestAllExtensions then parse it as TestAllTypes // it should work. - TestAllExtensionsLite message = TestUtil.getAllLiteExtensionsSet(); + TestAllExtensionsLite message = TestUtilLite.getAllLiteExtensionsSet(); ByteString rawBytes = message.toByteString(); assertEquals(rawBytes.size(), message.getSerializedSize()); @@ -144,7 +144,7 @@ public class WireFormatTest extends TestCase { public void testSerializePackedExtensionsLite() throws Exception { // TestPackedTypes and TestPackedExtensions should have compatible wire // formats; check that they serialize to the same string. - TestPackedExtensionsLite message = TestUtil.getLitePackedExtensionsSet(); + TestPackedExtensionsLite message = TestUtilLite.getLitePackedExtensionsSet(); ByteString rawBytes = message.toByteString(); TestPackedTypes message2 = TestUtil.getPackedSet(); @@ -190,7 +190,7 @@ public class WireFormatTest extends TestCase { TestAllTypes message = TestUtil.getAllSet(); ByteString rawBytes = message.toByteString(); - ExtensionRegistryLite registry_lite = TestUtil.getExtensionRegistryLite(); + ExtensionRegistryLite registry_lite = TestUtilLite.getExtensionRegistryLite(); TestAllExtensionsLite message2 = TestAllExtensionsLite.parseFrom(rawBytes, registry_lite); @@ -208,10 +208,10 @@ public class WireFormatTest extends TestCase { public void testParsePackedExtensionsLite() throws Exception { // Ensure that packed extensions can be properly parsed. - TestPackedExtensionsLite message = TestUtil.getLitePackedExtensionsSet(); + TestPackedExtensionsLite message = TestUtilLite.getLitePackedExtensionsSet(); ByteString rawBytes = message.toByteString(); - ExtensionRegistryLite registry = TestUtil.getExtensionRegistryLite(); + ExtensionRegistryLite registry = TestUtilLite.getExtensionRegistryLite(); TestPackedExtensionsLite message2 = TestPackedExtensionsLite.parseFrom(rawBytes, registry); diff --git a/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto b/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto index 868372505..6eef42c57 100644 --- a/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto +++ b/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto @@ -34,11 +34,15 @@ syntax = "proto2"; package protobuf_unittest.lite_equals_and_hash; -// This proto definition is used to test that java_generate_equals_and_hash -// works correctly with the LITE_RUNTIME. -option java_generate_equals_and_hash = true; option optimize_for = LITE_RUNTIME; +message TestOneofEquals { + oneof oneof_field { + string name = 1; + int32 value = 2; + } +} + message Foo { optional int32 value = 1; repeated Bar bar = 2; @@ -70,3 +74,8 @@ extend Foo { } } +message TestRecursiveOneof { + oneof Foo { + TestRecursiveOneof r = 1; + } +} diff --git a/java/lite/generate-sources-build.xml b/java/lite/generate-sources-build.xml new file mode 100644 index 000000000..89c21c131 --- /dev/null +++ b/java/lite/generate-sources-build.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/java/lite/generate-test-sources-build.xml b/java/lite/generate-test-sources-build.xml new file mode 100644 index 000000000..cdd1ee89a --- /dev/null +++ b/java/lite/generate-test-sources-build.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/lite/pom.xml b/java/lite/pom.xml index 70c7d047b..23cb4f78e 100644 --- a/java/lite/pom.xml +++ b/java/lite/pom.xml @@ -50,7 +50,7 @@ generate-sources - + @@ -64,7 +64,7 @@ generate-test-sources - + @@ -78,8 +78,8 @@ maven-compiler-plugin - ${generated.sources.dir} - ${generated.testsources.dir} + ${generated.sources.lite.dir} + ${generated.testsources.lite.dir} **/AbstractMessageLite.java **/AbstractParser.java diff --git a/java/pom.xml b/java/pom.xml index d5719edf0..e8dc5ded2 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -33,6 +33,8 @@ src/test/proto ${project.build.directory}/generated-sources ${project.build.directory}/generated-test-sources + ${project.build.directory}/generated-sources-lite + ${project.build.directory}/generated-test-sources-lite @@ -64,7 +66,7 @@ junit junit - 4.4 + 4.12 test 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 dc2f4b841..668d65ab1 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 @@ -60,10 +60,9 @@ import java.util.logging.Logger; * FieldMask in a message tree. */ class FieldMaskTree { - private static final Logger logger = - Logger.getLogger(FieldMaskTree.class.getName()); - - private static final String FIELD_PATH_SEPARATOR_REGEX = "\\."; + private static final Logger logger = Logger.getLogger(FieldMaskTree.class.getName()); + + private static final String FIELD_PATH_SEPARATOR_REGEX = "\\."; private static class Node { public TreeMap children = new TreeMap(); @@ -73,12 +72,12 @@ class FieldMaskTree { /** Creates an empty FieldMaskTree. */ public FieldMaskTree() {} - + /** Creates a FieldMaskTree for a given FieldMask. */ public FieldMaskTree(FieldMask mask) { mergeFromFieldMask(mask); } - + @Override public String toString() { return FieldMaskUtil.toString(toFieldMask()); @@ -121,7 +120,7 @@ class FieldMaskTree { node.children.clear(); return this; } - + /** * Merges all field paths in a FieldMask into this tree. */ @@ -149,8 +148,7 @@ class FieldMaskTree { return; } for (Entry entry : node.children.entrySet()) { - String childPath = path.isEmpty() - ? entry.getKey() : path + "." + entry.getKey(); + String childPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey(); getFieldPaths(entry.getValue(), childPath, paths); } } @@ -193,11 +191,10 @@ class FieldMaskTree { * Merges all fields specified by this FieldMaskTree from {@code source} to * {@code destination}. */ - public void merge(Message source, Message.Builder destination, - FieldMaskUtil.MergeOptions options) { + public void merge( + Message source, Message.Builder destination, FieldMaskUtil.MergeOptions options) { if (source.getDescriptorForType() != destination.getDescriptorForType()) { - throw new IllegalArgumentException( - "Cannot merge messages of different types."); + throw new IllegalArgumentException("Cannot merge messages of different types."); } if (root.children.isEmpty()) { return; @@ -208,30 +205,41 @@ class FieldMaskTree { /** Merges all fields specified by a sub-tree from {@code source} to * {@code destination}. */ - private void merge(Node node, String path, Message source, - Message.Builder destination, FieldMaskUtil.MergeOptions options) { + private void merge( + Node node, + String path, + Message source, + Message.Builder destination, + FieldMaskUtil.MergeOptions options) { assert source.getDescriptorForType() == destination.getDescriptorForType(); - + Descriptor descriptor = source.getDescriptorForType(); for (Entry entry : node.children.entrySet()) { - FieldDescriptor field = - descriptor.findFieldByName(entry.getKey()); + FieldDescriptor field = descriptor.findFieldByName(entry.getKey()); if (field == null) { - logger.warning("Cannot find field \"" + entry.getKey() - + "\" in message type " + descriptor.getFullName()); + logger.warning( + "Cannot find field \"" + + entry.getKey() + + "\" in message type " + + descriptor.getFullName()); continue; } if (!entry.getValue().children.isEmpty()) { - if (field.isRepeated() - || field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { - logger.warning("Field \"" + field.getFullName() + "\" is not a " - + "singluar message field and cannot have sub-fields."); + if (field.isRepeated() || field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { + logger.warning( + "Field \"" + + field.getFullName() + + "\" is not a " + + "singluar message field and cannot have sub-fields."); continue; } - String childPath = path.isEmpty() - ? entry.getKey() : path + "." + entry.getKey(); - merge(entry.getValue(), childPath, (Message) source.getField(field), - destination.getFieldBuilder(field), options); + String childPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey(); + merge( + entry.getValue(), + childPath, + (Message) source.getField(field), + destination.getFieldBuilder(field), + options); continue; } if (field.isRepeated()) { @@ -245,13 +253,22 @@ class FieldMaskTree { } else { if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { if (options.replaceMessageFields()) { - destination.setField(field, source.getField(field)); + if (!source.hasField(field)) { + destination.clearField(field); + } else { + destination.setField(field, source.getField(field)); + } } else { - destination.getFieldBuilder(field).mergeFrom( - (Message) source.getField(field)); + if (source.hasField(field)) { + destination.getFieldBuilder(field).mergeFrom((Message) source.getField(field)); + } } } else { - destination.setField(field, source.getField(field)); + if (source.hasField(field) || !options.replacePrimitiveFields()) { + destination.setField(field, source.getField(field)); + } else { + destination.clearField(field); + } } } } diff --git a/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java b/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java index 0b3060a76..969615210 100644 --- a/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java +++ b/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java @@ -211,14 +211,19 @@ public class FieldMaskUtil { public static FieldMask normalize(FieldMask mask) { return new FieldMaskTree(mask).toFieldMask(); } - + /** - * Creates an union of two FieldMasks. + * Creates a union of two or more FieldMasks. */ - public static FieldMask union(FieldMask mask1, FieldMask mask2) { - return new FieldMaskTree(mask1).mergeFromFieldMask(mask2).toFieldMask(); + public static FieldMask union( + FieldMask firstMask, FieldMask secondMask, FieldMask... otherMasks) { + FieldMaskTree maskTree = new FieldMaskTree(firstMask).mergeFromFieldMask(secondMask); + for (FieldMask mask : otherMasks) { + maskTree.mergeFromFieldMask(mask); + } + return maskTree.toFieldMask(); } - + /** * Calculates the intersection of two FieldMasks. */ @@ -237,6 +242,9 @@ public class FieldMaskUtil { public static final class MergeOptions { private boolean replaceMessageFields = false; private boolean replaceRepeatedFields = false; + // TODO(b/28277137): change the default behavior to always replace primitive fields after + // fixing all failing TAP tests. + private boolean replacePrimitiveFields = false; /** * Whether to replace message fields (i.e., discard existing content in @@ -257,7 +265,23 @@ public class FieldMaskUtil { public boolean replaceRepeatedFields() { return replaceRepeatedFields; } - + + /** + * Whether to replace primitive (non-repeated and non-message) fields in + * destination message fields with the source primitive fields (i.e., if the + * field is set in the source, the value is copied to the + * destination; if the field is unset in the source, the field is cleared + * from the destination) when merging. + * + *

Default behavior is to always set the value of the source primitive + * field to the destination primitive field, and if the source field is + * unset, the default value of the source field is copied to the + * destination. + */ + public boolean replacePrimitiveFields() { + return replacePrimitiveFields; + } + public void setReplaceMessageFields(boolean value) { replaceMessageFields = value; } @@ -265,10 +289,15 @@ public class FieldMaskUtil { public void setReplaceRepeatedFields(boolean value) { replaceRepeatedFields = value; } + + public void setReplacePrimitiveFields(boolean value) { + replacePrimitiveFields = value; + } } - + /** - * Merges fields specified by a FieldMask from one message to another. + * Merges fields specified by a FieldMask from one message to another with the + * specified merge options. */ public static void merge(FieldMask mask, Message source, Message.Builder destination, MergeOptions options) { 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 d13ff0ed0..76f3437a6 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 @@ -32,6 +32,7 @@ package com.google.protobuf.util; import com.google.common.io.BaseEncoding; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonNull; @@ -433,7 +434,7 @@ public class JsonFormat { private final Gson gson; private static class GsonHolder { - private static final Gson DEFAULT_GSON = new Gson(); + private static final Gson DEFAULT_GSON = new GsonBuilder().disableHtmlEscaping().create(); } PrinterImpl( @@ -951,16 +952,15 @@ public class JsonFormat { } } - private static final String TYPE_URL_PREFIX = "type.googleapis.com"; - + private static String getTypeName(String typeUrl) throws InvalidProtocolBufferException { String[] parts = typeUrl.split("/"); - if (parts.length != 2 || !parts[0].equals(TYPE_URL_PREFIX)) { + if (parts.length == 1) { throw new InvalidProtocolBufferException( "Invalid type url found: " + typeUrl); } - return parts[1]; + return parts[parts.length - 1]; } private static class ParserImpl { @@ -1066,6 +1066,15 @@ public class JsonFormat { parser.mergeStruct(json, builder); } }); + // Special-case ListValue. + parsers.put(ListValue.getDescriptor().getFullName(), + new WellKnownTypeParser() { + @Override + public void merge(ParserImpl parser, JsonElement json, + Message.Builder builder) throws InvalidProtocolBufferException { + parser.mergeListValue(json, builder); + } + }); // Special-case Value. parsers.put(Value.getDescriptor().getFullName(), new WellKnownTypeParser() { @@ -1213,6 +1222,16 @@ public class JsonFormat { } mergeMapField(field, json, builder); } + + private void mergeListValue(JsonElement json, Message.Builder builder) + throws InvalidProtocolBufferException { + Descriptor descriptor = builder.getDescriptorForType(); + FieldDescriptor field = descriptor.findFieldByName("values"); + if (field == null) { + throw new InvalidProtocolBufferException("Invalid ListValue type."); + } + mergeRepeatedField(field, json, builder); + } private void mergeValue(JsonElement json, Message.Builder builder) throws InvalidProtocolBufferException { @@ -1237,9 +1256,7 @@ public class JsonFormat { } else if (json instanceof JsonArray) { FieldDescriptor field = type.findFieldByName("list_value"); Message.Builder listBuilder = builder.newBuilderForField(field); - FieldDescriptor listField = - listBuilder.getDescriptorForType().findFieldByName("values"); - mergeRepeatedField(listField, json, listBuilder); + merge(json, listBuilder); builder.setField(field, listBuilder.build()); } else { throw new IllegalStateException("Unexpected json data: " + json); diff --git a/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java b/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java index 3391f239f..3ee0fc6e6 100644 --- a/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java +++ b/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java @@ -61,19 +61,16 @@ public class FieldMaskTreeTest extends TestCase { tree.addFieldPath("bar"); assertEquals("bar,foo", tree.toString()); } - + public void testMergeFromFieldMask() throws Exception { - FieldMaskTree tree = new FieldMaskTree( - FieldMaskUtil.fromString("foo,bar.baz,bar.quz")); + FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz")); assertEquals("bar.baz,bar.quz,foo", tree.toString()); - tree.mergeFromFieldMask( - FieldMaskUtil.fromString("foo.bar,bar")); + tree.mergeFromFieldMask(FieldMaskUtil.fromString("foo.bar,bar")); assertEquals("bar,foo", tree.toString()); } - + public void testIntersectFieldPath() throws Exception { - FieldMaskTree tree = new FieldMaskTree( - FieldMaskUtil.fromString("foo,bar.baz,bar.quz")); + FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz")); FieldMaskTree result = new FieldMaskTree(); // Empty path. tree.intersectFieldPath("", result); @@ -96,16 +93,18 @@ public class FieldMaskTreeTest extends TestCase { } public void testMerge() throws Exception { - TestAllTypes value = TestAllTypes.newBuilder() - .setOptionalInt32(1234) - .setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678)) - .addRepeatedInt32(4321) - .addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765)) - .build(); - NestedTestAllTypes source = NestedTestAllTypes.newBuilder() - .setPayload(value) - .setChild(NestedTestAllTypes.newBuilder().setPayload(value)) - .build(); + TestAllTypes value = + TestAllTypes.newBuilder() + .setOptionalInt32(1234) + .setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678)) + .addRepeatedInt32(4321) + .addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765)) + .build(); + NestedTestAllTypes source = + NestedTestAllTypes.newBuilder() + .setPayload(value) + .setChild(NestedTestAllTypes.newBuilder().setPayload(value)) + .build(); // Now we have a message source with the following structure: // [root] -+- payload -+- optional_int32 // | +- optional_nested_message @@ -116,114 +115,147 @@ public class FieldMaskTreeTest extends TestCase { // +- optional_nested_message // +- repeated_int32 // +- repeated_nested_message - + FieldMaskUtil.MergeOptions options = new FieldMaskUtil.MergeOptions(); - + // Test merging each individual field. NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder(); - new FieldMaskTree().addFieldPath("payload.optional_int32") - .merge(source, builder, options); + new FieldMaskTree().addFieldPath("payload.optional_int32").merge(source, builder, options); NestedTestAllTypes.Builder expected = NestedTestAllTypes.newBuilder(); expected.getPayloadBuilder().setOptionalInt32(1234); assertEquals(expected.build(), builder.build()); builder = NestedTestAllTypes.newBuilder(); - new FieldMaskTree().addFieldPath("payload.optional_nested_message") + new FieldMaskTree() + .addFieldPath("payload.optional_nested_message") .merge(source, builder, options); expected = NestedTestAllTypes.newBuilder(); - expected.getPayloadBuilder().setOptionalNestedMessage( - NestedMessage.newBuilder().setBb(5678)); + expected.getPayloadBuilder().setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678)); assertEquals(expected.build(), builder.build()); - builder = NestedTestAllTypes.newBuilder(); - new FieldMaskTree().addFieldPath("payload.repeated_int32") - .merge(source, builder, options); + new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options); expected = NestedTestAllTypes.newBuilder(); expected.getPayloadBuilder().addRepeatedInt32(4321); assertEquals(expected.build(), builder.build()); builder = NestedTestAllTypes.newBuilder(); - new FieldMaskTree().addFieldPath("payload.repeated_nested_message") + new FieldMaskTree() + .addFieldPath("payload.repeated_nested_message") .merge(source, builder, options); expected = NestedTestAllTypes.newBuilder(); - expected.getPayloadBuilder().addRepeatedNestedMessage( - NestedMessage.newBuilder().setBb(8765)); + expected.getPayloadBuilder().addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765)); assertEquals(expected.build(), builder.build()); builder = NestedTestAllTypes.newBuilder(); - new FieldMaskTree().addFieldPath("child.payload.optional_int32") + new FieldMaskTree() + .addFieldPath("child.payload.optional_int32") .merge(source, builder, options); expected = NestedTestAllTypes.newBuilder(); expected.getChildBuilder().getPayloadBuilder().setOptionalInt32(1234); assertEquals(expected.build(), builder.build()); builder = NestedTestAllTypes.newBuilder(); - new FieldMaskTree().addFieldPath("child.payload.optional_nested_message") + new FieldMaskTree() + .addFieldPath("child.payload.optional_nested_message") .merge(source, builder, options); expected = NestedTestAllTypes.newBuilder(); - expected.getChildBuilder().getPayloadBuilder().setOptionalNestedMessage( - NestedMessage.newBuilder().setBb(5678)); + expected + .getChildBuilder() + .getPayloadBuilder() + .setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678)); assertEquals(expected.build(), builder.build()); - builder = NestedTestAllTypes.newBuilder(); - new FieldMaskTree().addFieldPath("child.payload.repeated_int32") + new FieldMaskTree() + .addFieldPath("child.payload.repeated_int32") .merge(source, builder, options); expected = NestedTestAllTypes.newBuilder(); expected.getChildBuilder().getPayloadBuilder().addRepeatedInt32(4321); assertEquals(expected.build(), builder.build()); - builder = NestedTestAllTypes.newBuilder(); - new FieldMaskTree().addFieldPath("child.payload.repeated_nested_message") + new FieldMaskTree() + .addFieldPath("child.payload.repeated_nested_message") .merge(source, builder, options); expected = NestedTestAllTypes.newBuilder(); - expected.getChildBuilder().getPayloadBuilder().addRepeatedNestedMessage( - NestedMessage.newBuilder().setBb(8765)); + expected + .getChildBuilder() + .getPayloadBuilder() + .addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765)); assertEquals(expected.build(), builder.build()); - + // Test merging all fields. builder = NestedTestAllTypes.newBuilder(); - new FieldMaskTree().addFieldPath("child").addFieldPath("payload") - .merge(source, builder, options); + new FieldMaskTree() + .addFieldPath("child") + .addFieldPath("payload") + .merge(source, builder, options); assertEquals(source, builder.build()); - + // Test repeated options. builder = NestedTestAllTypes.newBuilder(); builder.getPayloadBuilder().addRepeatedInt32(1000); - new FieldMaskTree().addFieldPath("payload.repeated_int32") - .merge(source, builder, options); + new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options); // Default behavior is to append repeated fields. assertEquals(2, builder.getPayload().getRepeatedInt32Count()); assertEquals(1000, builder.getPayload().getRepeatedInt32(0)); assertEquals(4321, builder.getPayload().getRepeatedInt32(1)); // Change to replace repeated fields. options.setReplaceRepeatedFields(true); - new FieldMaskTree().addFieldPath("payload.repeated_int32") - .merge(source, builder, options); + new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options); assertEquals(1, builder.getPayload().getRepeatedInt32Count()); assertEquals(4321, builder.getPayload().getRepeatedInt32(0)); - + // Test message options. builder = NestedTestAllTypes.newBuilder(); builder.getPayloadBuilder().setOptionalInt32(1000); builder.getPayloadBuilder().setOptionalUint32(2000); - new FieldMaskTree().addFieldPath("payload") - .merge(source, builder, options); + new FieldMaskTree().addFieldPath("payload").merge(source, builder, options); // Default behavior is to merge message fields. assertEquals(1234, builder.getPayload().getOptionalInt32()); assertEquals(2000, builder.getPayload().getOptionalUint32()); - + + // Test merging unset message fields. + NestedTestAllTypes clearedSource = source.toBuilder().clearPayload().build(); + builder = NestedTestAllTypes.newBuilder(); + new FieldMaskTree().addFieldPath("payload").merge(clearedSource, builder, options); + assertEquals(false, builder.hasPayload()); + // Change to replace message fields. options.setReplaceMessageFields(true); builder = NestedTestAllTypes.newBuilder(); builder.getPayloadBuilder().setOptionalInt32(1000); builder.getPayloadBuilder().setOptionalUint32(2000); - new FieldMaskTree().addFieldPath("payload") - .merge(source, builder, options); + new FieldMaskTree().addFieldPath("payload").merge(source, builder, options); assertEquals(1234, builder.getPayload().getOptionalInt32()); assertEquals(0, builder.getPayload().getOptionalUint32()); + + // Test merging unset message fields. + builder = NestedTestAllTypes.newBuilder(); + builder.getPayloadBuilder().setOptionalInt32(1000); + builder.getPayloadBuilder().setOptionalUint32(2000); + new FieldMaskTree().addFieldPath("payload").merge(clearedSource, builder, options); + assertEquals(false, builder.hasPayload()); + + // Test merging unset primitive fields. + builder = source.toBuilder(); + builder.getPayloadBuilder().clearOptionalInt32(); + NestedTestAllTypes sourceWithPayloadInt32Unset = builder.build(); + builder = source.toBuilder(); + new FieldMaskTree() + .addFieldPath("payload.optional_int32") + .merge(sourceWithPayloadInt32Unset, builder, options); + assertEquals(true, builder.getPayload().hasOptionalInt32()); + assertEquals(0, builder.getPayload().getOptionalInt32()); + + // Change to clear unset primitive fields. + options.setReplacePrimitiveFields(true); + builder = source.toBuilder(); + new FieldMaskTree() + .addFieldPath("payload.optional_int32") + .merge(sourceWithPayloadInt32Unset, builder, options); + assertEquals(true, builder.hasPayload()); + assertEquals(false, builder.getPayload().hasOptionalInt32()); } } - diff --git a/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java b/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java index a312fc33a..194f7b9c7 100644 --- a/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java +++ b/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java @@ -152,6 +152,15 @@ public class FieldMaskUtilTest extends TestCase { FieldMask result = FieldMaskUtil.union(mask1, mask2); assertEquals("bar,foo", FieldMaskUtil.toString(result)); } + + public void testUnion_usingVarArgs() throws Exception { + FieldMask mask1 = FieldMaskUtil.fromString("foo"); + FieldMask mask2 = FieldMaskUtil.fromString("foo.bar,bar.quz"); + FieldMask mask3 = FieldMaskUtil.fromString("bar.quz"); + FieldMask mask4 = FieldMaskUtil.fromString("bar"); + FieldMask result = FieldMaskUtil.union(mask1, mask2, mask3, mask4); + assertEquals("bar,foo", FieldMaskUtil.toString(result)); + } public void testIntersection() throws Exception { // Only test a simple case here and expect 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 c0eb03306..d95b626c4 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 @@ -122,11 +122,11 @@ public class JsonFormatTest extends TestCase { builder.addRepeatedNestedEnum(NestedEnum.BAZ); builder.addRepeatedNestedMessageBuilder().setValue(200); } - + private void assertRoundTripEquals(Message message) throws Exception { assertRoundTripEquals(message, TypeRegistry.getEmptyTypeRegistry()); } - + private void assertRoundTripEquals(Message message, TypeRegistry registry) throws Exception { JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry); JsonFormat.Parser parser = JsonFormat.parser().usingTypeRegistry(registry); @@ -135,68 +135,68 @@ public class JsonFormatTest extends TestCase { Message parsedMessage = builder.build(); assertEquals(message.toString(), parsedMessage.toString()); } - + private String toJsonString(Message message) throws IOException { return JsonFormat.printer().print(message); } - + private void mergeFromJson(String json, Message.Builder builder) throws IOException { JsonFormat.parser().merge(json, builder); } - + public void testAllFields() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); setAllFields(builder); TestAllTypes message = builder.build(); - - assertEquals( + + assertEquals( "{\n" - + " \"optionalInt32\": 1234,\n" - + " \"optionalInt64\": \"1234567890123456789\",\n" - + " \"optionalUint32\": 5678,\n" - + " \"optionalUint64\": \"2345678901234567890\",\n" - + " \"optionalSint32\": 9012,\n" - + " \"optionalSint64\": \"3456789012345678901\",\n" - + " \"optionalFixed32\": 3456,\n" - + " \"optionalFixed64\": \"4567890123456789012\",\n" - + " \"optionalSfixed32\": 7890,\n" - + " \"optionalSfixed64\": \"5678901234567890123\",\n" - + " \"optionalFloat\": 1.5,\n" - + " \"optionalDouble\": 1.25,\n" - + " \"optionalBool\": true,\n" - + " \"optionalString\": \"Hello world!\",\n" - + " \"optionalBytes\": \"AAEC\",\n" - + " \"optionalNestedMessage\": {\n" - + " \"value\": 100\n" - + " },\n" - + " \"optionalNestedEnum\": \"BAR\",\n" - + " \"repeatedInt32\": [1234, 234],\n" - + " \"repeatedInt64\": [\"1234567890123456789\", \"234567890123456789\"],\n" - + " \"repeatedUint32\": [5678, 678],\n" - + " \"repeatedUint64\": [\"2345678901234567890\", \"345678901234567890\"],\n" - + " \"repeatedSint32\": [9012, 10],\n" - + " \"repeatedSint64\": [\"3456789012345678901\", \"456789012345678901\"],\n" - + " \"repeatedFixed32\": [3456, 456],\n" - + " \"repeatedFixed64\": [\"4567890123456789012\", \"567890123456789012\"],\n" - + " \"repeatedSfixed32\": [7890, 890],\n" - + " \"repeatedSfixed64\": [\"5678901234567890123\", \"678901234567890123\"],\n" - + " \"repeatedFloat\": [1.5, 11.5],\n" - + " \"repeatedDouble\": [1.25, 11.25],\n" - + " \"repeatedBool\": [true, true],\n" - + " \"repeatedString\": [\"Hello world!\", \"ello world!\"],\n" - + " \"repeatedBytes\": [\"AAEC\", \"AQI=\"],\n" - + " \"repeatedNestedMessage\": [{\n" - + " \"value\": 100\n" - + " }, {\n" - + " \"value\": 200\n" - + " }],\n" - + " \"repeatedNestedEnum\": [\"BAR\", \"BAZ\"]\n" - + "}", + + " \"optionalInt32\": 1234,\n" + + " \"optionalInt64\": \"1234567890123456789\",\n" + + " \"optionalUint32\": 5678,\n" + + " \"optionalUint64\": \"2345678901234567890\",\n" + + " \"optionalSint32\": 9012,\n" + + " \"optionalSint64\": \"3456789012345678901\",\n" + + " \"optionalFixed32\": 3456,\n" + + " \"optionalFixed64\": \"4567890123456789012\",\n" + + " \"optionalSfixed32\": 7890,\n" + + " \"optionalSfixed64\": \"5678901234567890123\",\n" + + " \"optionalFloat\": 1.5,\n" + + " \"optionalDouble\": 1.25,\n" + + " \"optionalBool\": true,\n" + + " \"optionalString\": \"Hello world!\",\n" + + " \"optionalBytes\": \"AAEC\",\n" + + " \"optionalNestedMessage\": {\n" + + " \"value\": 100\n" + + " },\n" + + " \"optionalNestedEnum\": \"BAR\",\n" + + " \"repeatedInt32\": [1234, 234],\n" + + " \"repeatedInt64\": [\"1234567890123456789\", \"234567890123456789\"],\n" + + " \"repeatedUint32\": [5678, 678],\n" + + " \"repeatedUint64\": [\"2345678901234567890\", \"345678901234567890\"],\n" + + " \"repeatedSint32\": [9012, 10],\n" + + " \"repeatedSint64\": [\"3456789012345678901\", \"456789012345678901\"],\n" + + " \"repeatedFixed32\": [3456, 456],\n" + + " \"repeatedFixed64\": [\"4567890123456789012\", \"567890123456789012\"],\n" + + " \"repeatedSfixed32\": [7890, 890],\n" + + " \"repeatedSfixed64\": [\"5678901234567890123\", \"678901234567890123\"],\n" + + " \"repeatedFloat\": [1.5, 11.5],\n" + + " \"repeatedDouble\": [1.25, 11.25],\n" + + " \"repeatedBool\": [true, true],\n" + + " \"repeatedString\": [\"Hello world!\", \"ello world!\"],\n" + + " \"repeatedBytes\": [\"AAEC\", \"AQI=\"],\n" + + " \"repeatedNestedMessage\": [{\n" + + " \"value\": 100\n" + + " }, {\n" + + " \"value\": 200\n" + + " }],\n" + + " \"repeatedNestedEnum\": [\"BAR\", \"BAZ\"]\n" + + "}", toJsonString(message)); - + assertRoundTripEquals(message); } - + public void testUnknownEnumValues() throws Exception { TestAllTypes message = TestAllTypes.newBuilder() .setOptionalNestedEnumValue(12345) @@ -209,21 +209,22 @@ public class JsonFormatTest extends TestCase { + " \"repeatedNestedEnum\": [12345, \"FOO\"]\n" + "}", toJsonString(message)); assertRoundTripEquals(message); - + TestMap.Builder mapBuilder = TestMap.newBuilder(); mapBuilder.getMutableInt32ToEnumMapValue().put(1, 0); mapBuilder.getMutableInt32ToEnumMapValue().put(2, 12345); TestMap mapMessage = mapBuilder.build(); assertEquals( - "{\n" - + " \"int32ToEnumMap\": {\n" - + " \"1\": \"FOO\",\n" - + " \"2\": 12345\n" - + " }\n" - + "}", toJsonString(mapMessage)); + "{\n" + + " \"int32ToEnumMap\": {\n" + + " \"1\": \"FOO\",\n" + + " \"2\": 12345\n" + + " }\n" + + "}", + toJsonString(mapMessage)); assertRoundTripEquals(mapMessage); } - + public void testSpecialFloatValues() throws Exception { TestAllTypes message = TestAllTypes.newBuilder() .addRepeatedFloat(Float.NaN) @@ -238,10 +239,10 @@ public class JsonFormatTest extends TestCase { + " \"repeatedFloat\": [\"NaN\", \"Infinity\", \"-Infinity\"],\n" + " \"repeatedDouble\": [\"NaN\", \"Infinity\", \"-Infinity\"]\n" + "}", toJsonString(message)); - + assertRoundTripEquals(message); } - + public void testParserAcceptStringForNumbericField() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); mergeFromJson( @@ -265,7 +266,7 @@ public class JsonFormatTest extends TestCase { assertEquals(1.25, message.getOptionalDouble()); assertEquals(true, message.getOptionalBool()); } - + public void testParserAcceptFloatingPointValueForIntegerField() throws Exception { // Test that numeric values like "1.000", "1e5" will also be accepted. TestAllTypes.Builder builder = TestAllTypes.newBuilder(); @@ -287,14 +288,14 @@ public class JsonFormatTest extends TestCase { assertEquals(expectedValues[i], builder.getRepeatedInt64(i)); assertEquals(expectedValues[i], builder.getRepeatedUint64(i)); } - + // Non-integers will still be rejected. assertRejects("optionalInt32", "1.5"); assertRejects("optionalUint32", "1.5"); assertRejects("optionalInt64", "1.5"); assertRejects("optionalUint64", "1.5"); } - + private void assertRejects(String name, String value) { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); try { @@ -312,7 +313,7 @@ public class JsonFormatTest extends TestCase { // Expected. } } - + private void assertAccepts(String name, String value) throws IOException { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); // Both numeric form and string form are accepted. @@ -320,17 +321,17 @@ public class JsonFormatTest extends TestCase { builder.clear(); mergeFromJson("{\"" + name + "\":\"" + value + "\"}", builder); } - + public void testParserRejectOutOfRangeNumericValues() throws Exception { assertAccepts("optionalInt32", String.valueOf(Integer.MAX_VALUE)); assertAccepts("optionalInt32", String.valueOf(Integer.MIN_VALUE)); assertRejects("optionalInt32", String.valueOf(Integer.MAX_VALUE + 1L)); assertRejects("optionalInt32", String.valueOf(Integer.MIN_VALUE - 1L)); - + assertAccepts("optionalUint32", String.valueOf(Integer.MAX_VALUE + 1L)); assertRejects("optionalUint32", "123456789012345"); assertRejects("optionalUint32", "-1"); - + BigInteger one = new BigInteger("1"); BigInteger maxLong = new BigInteger(String.valueOf(Long.MAX_VALUE)); BigInteger minLong = new BigInteger(String.valueOf(Long.MIN_VALUE)); @@ -351,7 +352,7 @@ public class JsonFormatTest extends TestCase { assertAccepts("optionalFloat", String.valueOf(-Float.MAX_VALUE)); assertRejects("optionalFloat", String.valueOf(Double.MAX_VALUE)); assertRejects("optionalFloat", String.valueOf(-Double.MAX_VALUE)); - + BigDecimal moreThanOne = new BigDecimal("1.000001"); BigDecimal maxDouble = new BigDecimal(Double.MAX_VALUE); BigDecimal minDouble = new BigDecimal(-Double.MAX_VALUE); @@ -360,7 +361,7 @@ public class JsonFormatTest extends TestCase { assertRejects("optionalDouble", maxDouble.multiply(moreThanOne).toString()); assertRejects("optionalDouble", minDouble.multiply(moreThanOne).toString()); } - + public void testParserAcceptNull() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); mergeFromJson( @@ -402,7 +403,7 @@ public class JsonFormatTest extends TestCase { + "}", builder); TestAllTypes message = builder.build(); assertEquals(TestAllTypes.getDefaultInstance(), message); - + // Repeated field elements cannot be null. try { builder = TestAllTypes.newBuilder(); @@ -414,7 +415,7 @@ public class JsonFormatTest extends TestCase { } catch (InvalidProtocolBufferException e) { // Exception expected. } - + try { builder = TestAllTypes.newBuilder(); mergeFromJson( @@ -426,14 +427,14 @@ public class JsonFormatTest extends TestCase { // Exception expected. } } - + public void testParserRejectDuplicatedFields() throws Exception { // TODO(xiaofeng): The parser we are currently using (GSON) will accept and keep the last // one if multiple entries have the same name. This is not the desired behavior but it can // only be fixed by using our own parser. Here we only test the cases where the names are // different but still referring to the same field. - - // Duplicated optional fields. + + // Duplicated optional fields. try { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); mergeFromJson( @@ -445,7 +446,7 @@ public class JsonFormatTest extends TestCase { } catch (InvalidProtocolBufferException e) { // Exception expected. } - + // Duplicated repeated fields. try { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); @@ -458,7 +459,7 @@ public class JsonFormatTest extends TestCase { } catch (InvalidProtocolBufferException e) { // Exception expected. } - + // Duplicated oneof fields. try { TestOneof.Builder builder = TestOneof.newBuilder(); @@ -472,7 +473,7 @@ public class JsonFormatTest extends TestCase { // Exception expected. } } - + public void testMapFields() throws Exception { TestMap.Builder builder = TestMap.newBuilder(); builder.getMutableInt32ToInt32Map().put(1, 10); @@ -507,7 +508,7 @@ public class JsonFormatTest extends TestCase { 8, NestedMessage.newBuilder().setValue(1234).build()); builder.getMutableInt32ToEnumMap().put(9, NestedEnum.BAR); TestMap message = builder.build(); - + assertEquals( "{\n" + " \"int32ToInt32Map\": {\n" @@ -598,13 +599,13 @@ public class JsonFormatTest extends TestCase { + " }\n" + "}", toJsonString(message)); assertRoundTripEquals(message); - + // Test multiple entries. builder = TestMap.newBuilder(); builder.getMutableInt32ToInt32Map().put(1, 2); builder.getMutableInt32ToInt32Map().put(3, 4); message = builder.build(); - + assertEquals( "{\n" + " \"int32ToInt32Map\": {\n" @@ -614,7 +615,7 @@ public class JsonFormatTest extends TestCase { + "}", toJsonString(message)); assertRoundTripEquals(message); } - + public void testMapNullValueIsRejected() throws Exception { try { TestMap.Builder builder = TestMap.newBuilder(); @@ -627,7 +628,7 @@ public class JsonFormatTest extends TestCase { } catch (InvalidProtocolBufferException e) { // Exception expected. } - + try { TestMap.Builder builder = TestMap.newBuilder(); mergeFromJson( @@ -640,7 +641,7 @@ public class JsonFormatTest extends TestCase { // Exception expected. } } - + public void testParserAcceptNonQuotedObjectKey() throws Exception { TestMap.Builder builder = TestMap.newBuilder(); mergeFromJson( @@ -652,7 +653,7 @@ public class JsonFormatTest extends TestCase { assertEquals(2, message.getInt32ToInt32Map().get(1).intValue()); assertEquals(3, message.getStringToInt32Map().get("hello").intValue()); } - + public void testWrappers() throws Exception { TestWrappers.Builder builder = TestWrappers.newBuilder(); builder.getBoolValueBuilder().setValue(false); @@ -665,7 +666,7 @@ public class JsonFormatTest extends TestCase { builder.getStringValueBuilder().setValue(""); builder.getBytesValueBuilder().setValue(ByteString.EMPTY); TestWrappers message = builder.build(); - + assertEquals( "{\n" + " \"int32Value\": 0,\n" @@ -691,7 +692,7 @@ public class JsonFormatTest extends TestCase { builder.getStringValueBuilder().setValue("7"); builder.getBytesValueBuilder().setValue(ByteString.copyFrom(new byte[]{8})); message = builder.build(); - + assertEquals( "{\n" + " \"int32Value\": 1,\n" @@ -706,43 +707,43 @@ public class JsonFormatTest extends TestCase { + "}", toJsonString(message)); assertRoundTripEquals(message); } - + public void testTimestamp() throws Exception { TestTimestamp message = TestTimestamp.newBuilder() .setTimestampValue(TimeUtil.parseTimestamp("1970-01-01T00:00:00Z")) .build(); - + assertEquals( "{\n" + " \"timestampValue\": \"1970-01-01T00:00:00Z\"\n" + "}", toJsonString(message)); assertRoundTripEquals(message); } - + public void testDuration() throws Exception { TestDuration message = TestDuration.newBuilder() .setDurationValue(TimeUtil.parseDuration("12345s")) .build(); - + assertEquals( "{\n" + " \"durationValue\": \"12345s\"\n" + "}", toJsonString(message)); assertRoundTripEquals(message); } - + public void testFieldMask() throws Exception { TestFieldMask message = TestFieldMask.newBuilder() .setFieldMaskValue(FieldMaskUtil.fromString("foo.bar,baz")) .build(); - + assertEquals( "{\n" + " \"fieldMaskValue\": \"foo.bar,baz\"\n" + "}", toJsonString(message)); assertRoundTripEquals(message); } - + public void testStruct() throws Exception { // Build a struct with all possible values. TestStruct.Builder builder = TestStruct.newBuilder(); @@ -764,7 +765,7 @@ public class JsonFormatTest extends TestCase { structBuilder.getMutableFields().put( "list_value", Value.newBuilder().setListValue(listBuilder.build()).build()); TestStruct message = builder.build(); - + assertEquals( "{\n" + " \"structValue\": {\n" @@ -778,7 +779,7 @@ public class JsonFormatTest extends TestCase { + " }\n" + "}", toJsonString(message)); assertRoundTripEquals(message); - + builder = TestStruct.newBuilder(); builder.setValue(Value.newBuilder().setNullValueValue(0).build()); message = builder.build(); @@ -787,12 +788,23 @@ public class JsonFormatTest extends TestCase { + " \"value\": null\n" + "}", toJsonString(message)); assertRoundTripEquals(message); + + builder = TestStruct.newBuilder(); + listBuilder = builder.getListValueBuilder(); + listBuilder.addValues(Value.newBuilder().setNumberValue(31831.125).build()); + listBuilder.addValues(Value.newBuilder().setNullValueValue(0).build()); + message = builder.build(); + assertEquals( + "{\n" + + " \"listValue\": [31831.125, null]\n" + + "}", toJsonString(message)); + assertRoundTripEquals(message); } - + public void testAnyFields() throws Exception { TestAllTypes content = TestAllTypes.newBuilder().setOptionalInt32(1234).build(); TestAny message = TestAny.newBuilder().setAnyValue(Any.pack(content)).build(); - + // A TypeRegistry must be provided in order to convert Any types. try { toJsonString(message); @@ -800,11 +812,11 @@ public class JsonFormatTest extends TestCase { } catch (IOException e) { // Expected. } - + JsonFormat.TypeRegistry registry = JsonFormat.TypeRegistry.newBuilder() .add(TestAllTypes.getDescriptor()).build(); JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry); - + assertEquals( "{\n" + " \"anyValue\": {\n" @@ -813,8 +825,8 @@ public class JsonFormatTest extends TestCase { + " }\n" + "}" , printer.print(message)); assertRoundTripEquals(message, registry); - - + + // Well-known types have a special formatting when embedded in Any. // // 1. Any in Any. @@ -828,7 +840,7 @@ public class JsonFormatTest extends TestCase { + " }\n" + "}", printer.print(anyMessage)); assertRoundTripEquals(anyMessage, registry); - + // 2. Wrappers in Any. anyMessage = Any.pack(Int32Value.newBuilder().setValue(12345).build()); assertEquals( @@ -894,7 +906,7 @@ public class JsonFormatTest extends TestCase { + " \"value\": \"AQI=\"\n" + "}", printer.print(anyMessage)); assertRoundTripEquals(anyMessage, registry); - + // 3. Timestamp in Any. anyMessage = Any.pack(TimeUtil.parseTimestamp("1969-12-31T23:59:59Z")); assertEquals( @@ -903,7 +915,7 @@ public class JsonFormatTest extends TestCase { + " \"value\": \"1969-12-31T23:59:59Z\"\n" + "}", printer.print(anyMessage)); assertRoundTripEquals(anyMessage, registry); - + // 4. Duration in Any anyMessage = Any.pack(TimeUtil.parseDuration("12345.10s")); assertEquals( @@ -945,7 +957,7 @@ public class JsonFormatTest extends TestCase { + "}", printer.print(anyMessage)); assertRoundTripEquals(anyMessage, registry); } - + public void testParserMissingTypeUrl() throws Exception { try { Any.Builder builder = Any.newBuilder(); @@ -958,7 +970,7 @@ public class JsonFormatTest extends TestCase { // Expected. } } - + public void testParserUnexpectedTypeUrl() throws Exception { try { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); @@ -970,9 +982,9 @@ public class JsonFormatTest extends TestCase { fail("Exception is expected."); } catch (IOException e) { // Expected. - } + } } - + public void testParserRejectTrailingComma() throws Exception { try { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); @@ -1000,13 +1012,13 @@ public class JsonFormatTest extends TestCase { // // Expected. // } } - + public void testParserRejectInvalidBase64() throws Exception { assertRejects("optionalBytes", "!@#$"); // We use standard BASE64 with paddings. assertRejects("optionalBytes", "AQI"); } - + public void testParserRejectInvalidEnumValue() throws Exception { try { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); @@ -1017,7 +1029,7 @@ public class JsonFormatTest extends TestCase { fail("Exception is expected."); } catch (InvalidProtocolBufferException e) { // Expected. - } + } } public void testCustomJsonName() throws Exception { @@ -1026,6 +1038,12 @@ public class JsonFormatTest extends TestCase { assertRoundTripEquals(message); } + public void testDefaultGsonDoesNotHtmlEscape() throws Exception { + TestAllTypes message = TestAllTypes.newBuilder().setOptionalString("=").build(); + assertEquals( + "{\n" + " \"optionalString\": \"=\"" + "\n}", JsonFormat.printer().print(message)); + } + public void testIncludingDefaultValueFields() throws Exception { TestAllTypes message = TestAllTypes.getDefaultInstance(); assertEquals("{\n}", JsonFormat.printer().print(message)); diff --git a/java/util/src/test/proto/com/google/protobuf/util/json_test.proto b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto index 509c1d697..686edc422 100644 --- a/java/util/src/test/proto/com/google/protobuf/util/json_test.proto +++ b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto @@ -28,6 +28,36 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// 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. + syntax = "proto3"; package json_test; @@ -159,6 +189,7 @@ message TestFieldMask { message TestStruct { google.protobuf.Struct struct_value = 1; google.protobuf.Value value = 2; + google.protobuf.ListValue list_value = 3; } message TestAny { diff --git a/jenkins/README.md b/jenkins/README.md new file mode 100644 index 000000000..29f664f29 --- /dev/null +++ b/jenkins/README.md @@ -0,0 +1,6 @@ + +Jenkins Infrastructure +---------------------- + +The scripts in this directory serve as plumbing for running the protobuf +tests under Jenkins. diff --git a/jenkins/build_and_run_docker.sh b/jenkins/build_and_run_docker.sh new file mode 100755 index 000000000..abc6f055c --- /dev/null +++ b/jenkins/build_and_run_docker.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# +# Builds docker image and runs a command under it. +# This is a generic script that is configured with the following variables: +# +# DOCKERFILE_DIR - Directory in which Dockerfile file is located. +# DOCKER_RUN_SCRIPT - Script to run under docker (relative to protobuf repo root) +# OUTPUT_DIR - Directory that will be copied from inside docker after finishing. +# $@ - Extra args to pass to docker run + + +set -ex + +cd $(dirname $0)/.. +git_root=$(pwd) +cd - + +# Use image name based on Dockerfile location checksum +DOCKER_IMAGE_NAME=$(basename $DOCKERFILE_DIR)_$(sha1sum $DOCKERFILE_DIR/Dockerfile | cut -f1 -d\ ) + +# Make sure docker image has been built. Should be instantaneous if so. +docker build -t $DOCKER_IMAGE_NAME $DOCKERFILE_DIR + +# Ensure existence of ccache directory +CCACHE_DIR=/tmp/protobuf-ccache +mkdir -p $CCACHE_DIR + +# Choose random name for docker container +CONTAINER_NAME="build_and_run_docker_$(uuidgen)" + +# Run command inside docker +docker run \ + "$@" \ + -e CCACHE_DIR=$CCACHE_DIR \ + -e EXTERNAL_GIT_ROOT="/var/local/jenkins/protobuf" \ + -e THIS_IS_REALLY_NEEDED='see https://github.com/docker/docker/issues/14203 for why docker is awful' \ + -v "$git_root:/var/local/jenkins/protobuf:ro" \ + -v $CCACHE_DIR:$CCACHE_DIR \ + -w /var/local/git/protobuf \ + --name=$CONTAINER_NAME \ + $DOCKER_IMAGE_NAME \ + bash -l "/var/local/jenkins/protobuf/$DOCKER_RUN_SCRIPT" || FAILED="true" + +# Copy output artifacts +if [ "$OUTPUT_DIR" != "" ] +then + docker cp "$CONTAINER_NAME:/var/local/git/protobuf/$OUTPUT_DIR" "$git_root" || FAILED="true" +fi + +# remove the container, possibly killing it first +docker rm -f $CONTAINER_NAME || true + +if [ "$FAILED" != "" ] +then + exit 1 +fi diff --git a/jenkins/buildcmds/README.md b/jenkins/buildcmds/README.md new file mode 100644 index 000000000..7a48f2d5c --- /dev/null +++ b/jenkins/buildcmds/README.md @@ -0,0 +1,6 @@ + +Jenkins Build Commands +---------------------- + +The scripts in this directory are designed to be top-level entry points for +Jenkins projects. diff --git a/jenkins/buildcmds/pull_request.sh b/jenkins/buildcmds/pull_request.sh new file mode 100755 index 000000000..01fda7985 --- /dev/null +++ b/jenkins/buildcmds/pull_request.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# +# This is the top-level script we give to Jenkins as the entry point for +# running the "pull request" project: +# +# https://grpc-testing.appspot.com/view/Protocol%20Buffers/job/protobuf_pull_request/ +# +# This script selects a specific Dockerfile (for building a Docker image) and +# a script to run inside that image. Then we delegate to the general +# build_and_run_docker.sh script. + +export DOCKERFILE_DIR=jenkins/docker +export DOCKER_RUN_SCRIPT=jenkins/pull_request_in_docker.sh +export OUTPUT_DIR=testoutput +./jenkins/build_and_run_docker.sh diff --git a/jenkins/docker/Dockerfile b/jenkins/docker/Dockerfile new file mode 100644 index 000000000..8467aeff5 --- /dev/null +++ b/jenkins/docker/Dockerfile @@ -0,0 +1,130 @@ +# This Dockerfile specifies the recipe for creating an image for the tests +# to run in. +# +# We install as many test dependencies here as we can, because these setup +# steps can be cached. They do *not* run every time we run the build. +# The Docker image is only rebuilt when the Dockerfile (ie. this file) +# changes. + +# Base Dockerfile for gRPC dev images +FROM debian:latest + +# Apt source for old Python versions. +RUN echo 'deb http://ppa.launchpad.net/fkrull/deadsnakes/ubuntu trusty main' > /etc/apt/sources.list.d/deadsnakes.list && \ + apt-key adv --keyserver keyserver.ubuntu.com --recv-keys DB82666C + +# Apt source for Oracle Java. +run echo 'deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main' > /etc/apt/sources.list.d/webupd8team-java-trusty.list && \ + apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EEA14886 && \ + echo "oracle-java7-installer shared/accepted-oracle-license-v1-1 select true" | debconf-set-selections + +# Apt source for Mono +run echo "deb http://download.mono-project.com/repo/debian wheezy main" | tee /etc/apt/sources.list.d/mono-xamarin.list && \ + echo "deb http://download.mono-project.com/repo/debian wheezy-libjpeg62-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list && \ + apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF + +# Install dependencies. We start with the basic ones require to build protoc +# and the C++ build +RUN apt-get update && apt-get install -y \ + autoconf \ + autotools-dev \ + build-essential \ + bzip2 \ + ccache \ + curl \ + gcc \ + git \ + libc6 \ + libc6-dbg \ + libc6-dev \ + libgtest-dev \ + libtool \ + make \ + parallel \ + time \ + wget \ + # -- For csharp -- + mono-devel \ + referenceassemblies-pcl \ + nunit \ + # -- For all Java builds -- \ + maven \ + # -- For java_jdk6 -- \ + # oops! not in jessie. too old? openjdk-6-jdk \ + # -- For java_jdk7 -- \ + openjdk-7-jdk \ + # -- For java_oracle7 -- \ + oracle-java7-installer \ + # -- For python / python_cpp -- \ + python-setuptools \ + python-pip \ + python-dev \ + python2.6-dev \ + python3.3-dev \ + python3.4-dev \ + # -- For Ruby -- + ruby \ + && apt-get clean + +################## +# C# dependencies + +RUN wget www.nuget.org/NuGet.exe -O /usr/local/bin/nuget.exe + +################## +# Python dependencies + +# These packages exist in apt-get, but their versions are too old, so we have +# to get updates from pip. + +RUN pip install pip --upgrade +RUN pip install virtualenv tox yattag + + +################## +# Ruby dependencies + +# Install rvm +RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 +RUN \curl -sSL https://get.rvm.io | bash -s stable + +# Install Ruby 2.1 +RUN /bin/bash -l -c "rvm install ruby-2.1" +RUN /bin/bash -l -c "rvm use --default ruby-2.1" +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" +RUN /bin/bash -l -c "echo 'rvm --default use ruby-2.1' >> ~/.bashrc" +RUN /bin/bash -l -c "gem install bundler --no-ri --no-rdoc" + +################## +# Java dependencies + +# This step requires compiling protoc. :( + +ENV MAVEN_REPO /var/maven_local_repository +ENV MVN mvn --batch-mode + +RUN cd /tmp && \ + git clone https://github.com/google/protobuf.git && \ + cd protobuf && \ + ./autogen.sh && \ + ./configure && \ + make -j6 && \ + cd java && \ + $MVN install dependency:go-offline -Dmaven.repo.local=$MAVEN_REPO -P lite && \ + $MVN install dependency:go-offline -Dmaven.repo.local=$MAVEN_REPO && \ + cd ../javanano && \ + $MVN install dependency:go-offline -Dmaven.repo.local=$MAVEN_REPO + +################## +# Prepare ccache + +RUN ln -s /usr/bin/ccache /usr/local/bin/gcc +RUN ln -s /usr/bin/ccache /usr/local/bin/g++ +RUN ln -s /usr/bin/ccache /usr/local/bin/cc +RUN ln -s /usr/bin/ccache /usr/local/bin/c++ +RUN ln -s /usr/bin/ccache /usr/local/bin/clang +RUN ln -s /usr/bin/ccache /usr/local/bin/clang++ + +# Define the default command. +CMD ["bash"] diff --git a/jenkins/make_test_output.py b/jenkins/make_test_output.py new file mode 100644 index 000000000..b1f2e2c0b --- /dev/null +++ b/jenkins/make_test_output.py @@ -0,0 +1,91 @@ +"""Gathers output from test runs and create an XML file in JUnit format. + +The output files from the individual tests have been written in a directory +structure like: + + $DIR/joblog (output from "parallel --joblog joblog") + $DIR/logs/1/cpp/stdout + $DIR/logs/1/cpp/stderr + $DIR/logs/1/csharp/stdout + $DIR/logs/1/csharp/stderr + $DIR/logs/1/java_jdk7/stdout + $DIR/logs/1/java_jdk7/stderr + etc. + +This script bundles them into a single output XML file so Jenkins can show +detailed test results. It runs as the last step before the Jenkins build +finishes. +""" + +import os; +import sys; +from yattag import Doc +from collections import defaultdict + +def readtests(basedir): + tests = defaultdict(dict) + + # Sample input (note: separators are tabs). + # + # Seq Host Starttime Runtime Send Receive Exitval Signal Command + # 1 : 1456263838.313 0.005 0 0 0 0 echo A + with open(basedir + "/joblog") as jobs: + firstline = next(jobs) + for line in jobs: + values = line.split("\t") + + name = values[8].split()[-1] + test = tests[name] + test["name"] = name + test["time"] = values[3] + + exitval = values[6] + if int(exitval): + # We don't have a more specific message. User should look at stderr. + test["failure"] = "TEST FAILURE" + else: + test["failure"] = False + + for testname in os.listdir(basedir + "/logs/1"): + test = tests[testname] + + with open(basedir + "/logs/1/" + testname + "/stdout") as f: + test["stdout"] = f.read() + + with open(basedir + "/logs/1/" + testname + "/stderr") as f: + test["stderr"] = f.read() + + # The cpp test is special since it doesn't run under parallel so doesn't show + # up in the job log. + tests["cpp"]["name"] = "cpp" + + with open(basedir + '/logs/1/cpp/build_time', 'r') as f: + tests["cpp"]["time"] = f.read().strip() + tests["cpp"]["failure"] = False + + ret = tests.values() + ret.sort(key=lambda x: x["name"]) + + return ret + +def genxml(tests): + doc, tag, text = Doc().tagtext() + + with tag("testsuites"): + with tag("testsuite", name="Protobuf Tests"): + for test in tests: + with tag("testcase", name=test["name"], classname=test["name"], + time=test["time"]): + with tag("system-out"): + text(test["stdout"]) + with tag("system-err"): + text(test["stderr"]) + if test["failure"]: + with tag("failure"): + text(test["failure"]) + + return doc.getvalue() + +sys.stderr.write("make_test_output.py: writing XML from directory: " + + sys.argv[1] + "\n"); +print genxml(readtests(sys.argv[1])) diff --git a/jenkins/pull_request_in_docker.sh b/jenkins/pull_request_in_docker.sh new file mode 100755 index 000000000..887f97c59 --- /dev/null +++ b/jenkins/pull_request_in_docker.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# +# This is the script that runs inside Docker, once the image has been built, +# to execute all tests for the "pull request" project. + +WORKSPACE_BASE=`pwd` +MY_DIR="$(dirname "$0")" +TEST_SCRIPT=$MY_DIR/../tests.sh +BUILD_DIR=/tmp/protobuf + +set -e # exit immediately on error +set -x # display all commands + +# The protobuf repository is mounted into our Docker image, but read-only. +# We clone into a directory inside Docker (this is faster than cp). +rm -rf $BUILD_DIR +mkdir -p $BUILD_DIR +cd $BUILD_DIR +git clone /var/local/jenkins/protobuf +cd protobuf + +# Set up the directory where our test output is going to go. +OUTPUT_DIR=`mktemp -d` +LOG_OUTPUT_DIR=$OUTPUT_DIR/logs +mkdir -p $LOG_OUTPUT_DIR/1/cpp + +################################################################################ +# cpp build needs to run first, non-parallelized, so that protoc is available +# for other builds. + +# Output filenames to follow the overall scheme used by parallel, ie: +# $DIR/logs/1/cpp/stdout +# $DIR/logs/1/cpp/stderr +# $DIR/logs/1/csharp/stdout +# $DIR/logs/1/csharp/stderr +# $DIR/logs/1/java_jdk7/stdout +# $DIR/logs/1/java_jdk7/stderr +CPP_STDOUT=$LOG_OUTPUT_DIR/1/cpp/stdout +CPP_STDERR=$LOG_OUTPUT_DIR/1/cpp/stderr + +# Time the C++ build, so we can put this info in the test output. +# It's important that we get /usr/bin/time (which supports -f and -o) and not +# the bash builtin "time" which doesn't. +TIME_CMD="/usr/bin/time -f %e -o $LOG_OUTPUT_DIR/1/cpp/build_time" + +$TIME_CMD $TEST_SCRIPT cpp > >(tee $CPP_STDOUT) 2> >(tee $CPP_STDERR >&2) + +# Other tests are run in parallel. + +parallel --results $LOG_OUTPUT_DIR --joblog $OUTPUT_DIR/joblog $TEST_SCRIPT ::: \ + csharp \ + java_jdk7 \ + javanano_jdk7 \ + java_oracle7 \ + javanano_oracle7 \ + python \ + python_cpp \ + ruby21 \ + || true # Process test results even if tests fail. + +cat $OUTPUT_DIR/joblog + +# The directory that is copied from Docker back into the Jenkins workspace. +COPY_FROM_DOCKER=/var/local/git/protobuf/testoutput +mkdir -p $COPY_FROM_DOCKER +TESTOUTPUT_XML_FILE=$COPY_FROM_DOCKER/testresults.xml + +# Process all the output files from "parallel" and package them into a single +# .xml file with detailed, broken-down test output. +python $MY_DIR/make_test_output.py $OUTPUT_DIR > $TESTOUTPUT_XML_FILE + +ls -l $TESTOUTPUT_XML_FILE diff --git a/js/binary/constants.js b/js/binary/constants.js index a976e0b60..836216bfa 100644 --- a/js/binary/constants.js +++ b/js/binary/constants.js @@ -41,11 +41,16 @@ goog.provide('jspb.BinaryMessage'); goog.provide('jspb.BuilderFunction'); goog.provide('jspb.ByteSource'); goog.provide('jspb.ClonerFunction'); +goog.provide('jspb.ComparerFunction'); goog.provide('jspb.ConstBinaryMessage'); +goog.provide('jspb.PrunerFunction'); goog.provide('jspb.ReaderFunction'); goog.provide('jspb.RecyclerFunction'); +goog.provide('jspb.RepeatedFieldType'); +goog.provide('jspb.ScalarFieldType'); goog.provide('jspb.WriterFunction'); + goog.forwardDeclare('jspb.Message'); goog.forwardDeclare('jsproto.BinaryExtension'); @@ -78,12 +83,30 @@ jspb.BinaryMessage = function() {}; jspb.ByteSource; +/** + * A scalar field in jspb can be a boolean, number, or string. + * @typedef {boolean|number|string} + */ +jspb.ScalarFieldType; + + +/** + * A repeated field in jspb is an array of scalars, blobs, or messages. + * @typedef {!Array| + !Array| + !Array} + */ +jspb.RepeatedFieldType; + + /** * A field in jspb can be a scalar, a block of bytes, another proto, or an * array of any of the above. - * @typedef {boolean|number|string|Uint8Array| - jspb.BinaryMessage|jsproto.BinaryExtension| - Array} + * @typedef {jspb.ScalarFieldType| + jspb.RepeatedFieldType| + !Uint8Array| + !jspb.BinaryMessage| + !jsproto.BinaryExtension} */ jspb.AnyFieldType; @@ -124,6 +147,23 @@ jspb.ReaderFunction; jspb.WriterFunction; +/** + * A pruner function removes default-valued fields and empty submessages from a + * message and returns either the pruned message or null if the entire message + * was pruned away. + * @typedef {function(?jspb.BinaryMessage):?jspb.BinaryMessage} + */ +jspb.PrunerFunction; + + +/** + * A comparer function returns true if two protos are equal. + * @typedef {!function(?jspb.ConstBinaryMessage, + * ?jspb.ConstBinaryMessage):boolean} + */ +jspb.ComparerFunction; + + /** * Field type codes, taken from proto2/public/wire_format_lite.h. * @enum {number} diff --git a/js/binary/decoder.js b/js/binary/decoder.js index 9004eff00..41094a368 100644 --- a/js/binary/decoder.js +++ b/js/binary/decoder.js @@ -223,7 +223,7 @@ jspb.BinaryIterator.prototype.next = function() { jspb.BinaryDecoder = function(opt_bytes, opt_start, opt_length) { /** * Typed byte-wise view of the source buffer. - * @private {Uint8Array} + * @private {?Uint8Array} */ this.bytes_ = null; @@ -335,7 +335,7 @@ jspb.BinaryDecoder.prototype.clear = function() { /** * Returns the raw buffer. - * @return {Uint8Array} The raw buffer. + * @return {?Uint8Array} The raw buffer. */ jspb.BinaryDecoder.prototype.getBuffer = function() { return this.bytes_; @@ -631,6 +631,7 @@ jspb.BinaryDecoder.prototype.readUnsignedVarint32String = function() { return value.toString(); }; + /** * Reads a 32-bit signed variant and returns its value as a string. * @@ -950,14 +951,15 @@ jspb.BinaryDecoder.prototype.readStringWithLength = function() { * Reads a block of raw bytes from the binary stream. * * @param {number} length The number of bytes to read. - * @return {Uint8Array} The decoded block of bytes, or null if the length was - * invalid. + * @return {!Uint8Array} The decoded block of bytes, or an empty block if the + * length was invalid. */ jspb.BinaryDecoder.prototype.readBytes = function(length) { if (length < 0 || this.cursor_ + length > this.bytes_.length) { this.error_ = true; - return null; + goog.asserts.fail('Invalid byte length!'); + return new Uint8Array(0); } var result = this.bytes_.subarray(this.cursor_, this.cursor_ + length); diff --git a/js/binary/decoder_test.js b/js/binary/decoder_test.js index 27342e493..d045e912c 100644 --- a/js/binary/decoder_test.js +++ b/js/binary/decoder_test.js @@ -44,11 +44,11 @@ goog.require('goog.testing.asserts'); goog.require('jspb.BinaryConstants'); goog.require('jspb.BinaryDecoder'); -goog.require('jspb.BinaryWriter'); +goog.require('jspb.BinaryEncoder'); /** - * Tests raw encoding and decoding of unsigned types. + * Tests encoding and decoding of unsigned types. * @param {Function} readValue * @param {Function} writeValue * @param {number} epsilon @@ -58,34 +58,38 @@ goog.require('jspb.BinaryWriter'); */ function doTestUnsignedValue(readValue, writeValue, epsilon, upperLimit, filter) { - var writer = new jspb.BinaryWriter(); + var encoder = new jspb.BinaryEncoder(); // Encode zero and limits. - writeValue.call(writer, filter(0)); - writeValue.call(writer, filter(epsilon)); - writeValue.call(writer, filter(upperLimit)); + writeValue.call(encoder, filter(0)); + writeValue.call(encoder, filter(epsilon)); + writeValue.call(encoder, filter(upperLimit)); // Encode positive values. for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { - writeValue.call(writer, filter(cursor)); + writeValue.call(encoder, filter(cursor)); } - var reader = jspb.BinaryDecoder.alloc(writer.getResultBuffer()); + var decoder = jspb.BinaryDecoder.alloc(encoder.end()); // Check zero and limits. - assertEquals(filter(0), readValue.call(reader)); - assertEquals(filter(epsilon), readValue.call(reader)); - assertEquals(filter(upperLimit), readValue.call(reader)); + assertEquals(filter(0), readValue.call(decoder)); + assertEquals(filter(epsilon), readValue.call(decoder)); + assertEquals(filter(upperLimit), readValue.call(decoder)); // Check positive values. for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { - if (filter(cursor) != readValue.call(reader)) throw 'fail!'; + if (filter(cursor) != readValue.call(decoder)) throw 'fail!'; } + + // Encoding values outside the valid range should assert. + assertThrows(function() {writeValue.call(encoder, -1);}); + assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);}); } /** - * Tests raw encoding and decoding of signed types. + * Tests encoding and decoding of signed types. * @param {Function} readValue * @param {Function} writeValue * @param {number} epsilon @@ -96,44 +100,48 @@ function doTestUnsignedValue(readValue, */ function doTestSignedValue(readValue, writeValue, epsilon, lowerLimit, upperLimit, filter) { - var writer = new jspb.BinaryWriter(); + var encoder = new jspb.BinaryEncoder(); // Encode zero and limits. - writeValue.call(writer, filter(lowerLimit)); - writeValue.call(writer, filter(-epsilon)); - writeValue.call(writer, filter(0)); - writeValue.call(writer, filter(epsilon)); - writeValue.call(writer, filter(upperLimit)); + writeValue.call(encoder, filter(lowerLimit)); + writeValue.call(encoder, filter(-epsilon)); + writeValue.call(encoder, filter(0)); + writeValue.call(encoder, filter(epsilon)); + writeValue.call(encoder, filter(upperLimit)); var inputValues = []; // Encode negative values. for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) { var val = filter(cursor); - writeValue.call(writer, val); + writeValue.call(encoder, val); inputValues.push(val); } // Encode positive values. for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) { var val = filter(cursor); - writeValue.call(writer, val); + writeValue.call(encoder, val); inputValues.push(val); } - var reader = jspb.BinaryDecoder.alloc(writer.getResultBuffer()); + var decoder = jspb.BinaryDecoder.alloc(encoder.end()); // Check zero and limits. - assertEquals(filter(lowerLimit), readValue.call(reader)); - assertEquals(filter(-epsilon), readValue.call(reader)); - assertEquals(filter(0), readValue.call(reader)); - assertEquals(filter(epsilon), readValue.call(reader)); - assertEquals(filter(upperLimit), readValue.call(reader)); + assertEquals(filter(lowerLimit), readValue.call(decoder)); + assertEquals(filter(-epsilon), readValue.call(decoder)); + assertEquals(filter(0), readValue.call(decoder)); + assertEquals(filter(epsilon), readValue.call(decoder)); + assertEquals(filter(upperLimit), readValue.call(decoder)); // Verify decoded values. for (var i = 0; i < inputValues.length; i++) { - assertEquals(inputValues[i], readValue.call(reader)); + assertEquals(inputValues[i], readValue.call(decoder)); } + + // Encoding values outside the valid range should assert. + assertThrows(function() {writeValue.call(encoder, lowerLimit * 1.1);}); + assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);}); } describe('binaryDecoderTest', function() { @@ -169,7 +177,7 @@ describe('binaryDecoderTest', function() { * Tests reading 64-bit integers as hash strings. */ it('testHashStrings', function() { - var writer = new jspb.BinaryWriter(); + var encoder = new jspb.BinaryEncoder(); var hashA = String.fromCharCode(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); @@ -180,17 +188,17 @@ describe('binaryDecoderTest', function() { var hashD = String.fromCharCode(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); - writer.rawWriteVarintHash64(hashA); - writer.rawWriteVarintHash64(hashB); - writer.rawWriteVarintHash64(hashC); - writer.rawWriteVarintHash64(hashD); + encoder.writeVarintHash64(hashA); + encoder.writeVarintHash64(hashB); + encoder.writeVarintHash64(hashC); + encoder.writeVarintHash64(hashD); - writer.rawWriteFixedHash64(hashA); - writer.rawWriteFixedHash64(hashB); - writer.rawWriteFixedHash64(hashC); - writer.rawWriteFixedHash64(hashD); + encoder.writeFixedHash64(hashA); + encoder.writeFixedHash64(hashB); + encoder.writeFixedHash64(hashC); + encoder.writeFixedHash64(hashD); - var decoder = jspb.BinaryDecoder.alloc(writer.getResultBuffer()); + var decoder = jspb.BinaryDecoder.alloc(encoder.end()); assertEquals(hashA, decoder.readVarintHash64()); assertEquals(hashB, decoder.readVarintHash64()); @@ -214,8 +222,8 @@ describe('binaryDecoderTest', function() { assertThrows(function() {decoder.readUint64()}); // Overlong varints should trigger assertions. - decoder.setBlock( - [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0]); + decoder.setBlock([255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0]); assertThrows(function() {decoder.readUnsignedVarint64()}); decoder.reset(); assertThrows(function() {decoder.readSignedVarint64()}); @@ -244,61 +252,61 @@ describe('binaryDecoderTest', function() { /** - * Tests raw encoding and decoding of unsigned integers. + * Tests encoding and decoding of unsigned integers. */ - it('testRawUnsigned', function() { + it('testUnsignedIntegers', function() { doTestUnsignedValue( jspb.BinaryDecoder.prototype.readUint8, - jspb.BinaryWriter.prototype.rawWriteUint8, + jspb.BinaryEncoder.prototype.writeUint8, 1, 0xFF, Math.round); doTestUnsignedValue( jspb.BinaryDecoder.prototype.readUint16, - jspb.BinaryWriter.prototype.rawWriteUint16, + jspb.BinaryEncoder.prototype.writeUint16, 1, 0xFFFF, Math.round); doTestUnsignedValue( jspb.BinaryDecoder.prototype.readUint32, - jspb.BinaryWriter.prototype.rawWriteUint32, + jspb.BinaryEncoder.prototype.writeUint32, 1, 0xFFFFFFFF, Math.round); doTestUnsignedValue( jspb.BinaryDecoder.prototype.readUint64, - jspb.BinaryWriter.prototype.rawWriteUint64, + jspb.BinaryEncoder.prototype.writeUint64, 1, Math.pow(2, 64) - 1025, Math.round); }); /** - * Tests raw encoding and decoding of signed integers. + * Tests encoding and decoding of signed integers. */ - it('testRawSigned', function() { + it('testSignedIntegers', function() { doTestSignedValue( jspb.BinaryDecoder.prototype.readInt8, - jspb.BinaryWriter.prototype.rawWriteInt8, + jspb.BinaryEncoder.prototype.writeInt8, 1, -0x80, 0x7F, Math.round); doTestSignedValue( jspb.BinaryDecoder.prototype.readInt16, - jspb.BinaryWriter.prototype.rawWriteInt16, + jspb.BinaryEncoder.prototype.writeInt16, 1, -0x8000, 0x7FFF, Math.round); doTestSignedValue( jspb.BinaryDecoder.prototype.readInt32, - jspb.BinaryWriter.prototype.rawWriteInt32, + jspb.BinaryEncoder.prototype.writeInt32, 1, -0x80000000, 0x7FFFFFFF, Math.round); doTestSignedValue( jspb.BinaryDecoder.prototype.readInt64, - jspb.BinaryWriter.prototype.rawWriteInt64, + jspb.BinaryEncoder.prototype.writeInt64, 1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round); }); /** - * Tests raw encoding and decoding of floats. + * Tests encoding and decoding of floats. */ - it('testRawFloats', function() { + it('testFloats', function() { /** * @param {number} x * @return {number} @@ -310,7 +318,7 @@ describe('binaryDecoderTest', function() { } doTestSignedValue( jspb.BinaryDecoder.prototype.readFloat, - jspb.BinaryWriter.prototype.rawWriteFloat, + jspb.BinaryEncoder.prototype.writeFloat, jspb.BinaryConstants.FLOAT32_EPS, -jspb.BinaryConstants.FLOAT32_MAX, jspb.BinaryConstants.FLOAT32_MAX, @@ -318,7 +326,7 @@ describe('binaryDecoderTest', function() { doTestSignedValue( jspb.BinaryDecoder.prototype.readDouble, - jspb.BinaryWriter.prototype.rawWriteDouble, + jspb.BinaryEncoder.prototype.writeDouble, jspb.BinaryConstants.FLOAT64_EPS * 10, -jspb.BinaryConstants.FLOAT64_MAX, jspb.BinaryConstants.FLOAT64_MAX, diff --git a/js/binary/encoder.js b/js/binary/encoder.js new file mode 100644 index 000000000..c9b0c2aee --- /dev/null +++ b/js/binary/encoder.js @@ -0,0 +1,430 @@ +// 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. + +/** + * @fileoverview BinaryEncode defines methods for encoding Javascript values + * into arrays of bytes compatible with the Protocol Buffer wire format. + * + * @author aappleby@google.com (Austin Appleby) + */ + +goog.provide('jspb.BinaryEncoder'); + +goog.require('goog.asserts'); +goog.require('jspb.BinaryConstants'); +goog.require('jspb.utils'); + + + +/** + * BinaryEncoder implements encoders for all the wire types specified in + * https://developers.google.com/protocol-buffers/docs/encoding. + * + * @constructor + * @struct + */ +jspb.BinaryEncoder = function() { + /** @private {!Array.} */ + this.buffer_ = []; +}; + + +/** + * @return {number} + */ +jspb.BinaryEncoder.prototype.length = function() { + return this.buffer_.length; +}; + + +/** + * @return {!Array.} + */ +jspb.BinaryEncoder.prototype.end = function() { + var buffer = this.buffer_; + this.buffer_ = []; + return buffer; +}; + + +/** + * Encodes a 64-bit integer in 32:32 split representation into its wire-format + * varint representation and stores it in the buffer. + * @param {number} lowBits The low 32 bits of the int. + * @param {number} highBits The high 32 bits of the int. + */ +jspb.BinaryEncoder.prototype.writeSplitVarint64 = function(lowBits, highBits) { + goog.asserts.assert(lowBits == Math.floor(lowBits)); + goog.asserts.assert(highBits == Math.floor(highBits)); + goog.asserts.assert((lowBits >= 0) && + (lowBits < jspb.BinaryConstants.TWO_TO_32)); + goog.asserts.assert((highBits >= 0) && + (highBits < jspb.BinaryConstants.TWO_TO_32)); + + // Break the binary representation into chunks of 7 bits, set the 8th bit + // in each chunk if it's not the final chunk, and append to the result. + while (highBits > 0 || lowBits > 127) { + this.buffer_.push((lowBits & 0x7f) | 0x80); + lowBits = ((lowBits >>> 7) | (highBits << 25)) >>> 0; + highBits = highBits >>> 7; + } + this.buffer_.push(lowBits); +}; + + +/** + * Encodes a 32-bit unsigned integer into its wire-format varint representation + * and stores it in the buffer. + * @param {number} value The integer to convert. + */ +jspb.BinaryEncoder.prototype.writeUnsignedVarint32 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= 0) && + (value < jspb.BinaryConstants.TWO_TO_32)); + + while (value > 127) { + this.buffer_.push((value & 0x7f) | 0x80); + value = value >>> 7; + } + + this.buffer_.push(value); +}; + + +/** + * Encodes a 32-bit signed integer into its wire-format varint representation + * and stores it in the buffer. + * @param {number} value The integer to convert. + */ +jspb.BinaryEncoder.prototype.writeSignedVarint32 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) && + (value < jspb.BinaryConstants.TWO_TO_31)); + + // Use the unsigned version if the value is not negative. + if (value >= 0) { + this.writeUnsignedVarint32(value); + return; + } + + // Write nine bytes with a _signed_ right shift so we preserve the sign bit. + for (var i = 0; i < 9; i++) { + this.buffer_.push((value & 0x7f) | 0x80); + value = value >> 7; + } + + // The above loop writes out 63 bits, so the last byte is always the sign bit + // which is always set for negative numbers. + this.buffer_.push(1); +}; + + +/** + * Encodes a 64-bit unsigned integer into its wire-format varint representation + * and stores it in the buffer. Integers that are not representable in 64 bits + * will be truncated. + * @param {number} value The integer to convert. + */ +jspb.BinaryEncoder.prototype.writeUnsignedVarint64 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= 0) && + (value < jspb.BinaryConstants.TWO_TO_64)); + jspb.utils.splitInt64(value); + this.writeSplitVarint64(jspb.utils.split64Low, + jspb.utils.split64High); +}; + + +/** + * Encodes a 64-bit signed integer into its wire-format varint representation + * and stores it in the buffer. Integers that are not representable in 64 bits + * will be truncated. + * @param {number} value The integer to convert. + */ +jspb.BinaryEncoder.prototype.writeSignedVarint64 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) && + (value < jspb.BinaryConstants.TWO_TO_63)); + jspb.utils.splitInt64(value); + this.writeSplitVarint64(jspb.utils.split64Low, + jspb.utils.split64High); +}; + + +/** + * Encodes a JavaScript integer into its wire-format, zigzag-encoded varint + * representation and stores it in the buffer. + * @param {number} value The integer to convert. + */ +jspb.BinaryEncoder.prototype.writeZigzagVarint32 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) && + (value < jspb.BinaryConstants.TWO_TO_31)); + this.writeUnsignedVarint32(((value << 1) ^ (value >> 31)) >>> 0); +}; + + +/** + * Encodes a JavaScript integer into its wire-format, zigzag-encoded varint + * representation and stores it in the buffer. Integers not representable in 64 + * bits will be truncated. + * @param {number} value The integer to convert. + */ +jspb.BinaryEncoder.prototype.writeZigzagVarint64 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) && + (value < jspb.BinaryConstants.TWO_TO_63)); + jspb.utils.splitZigzag64(value); + this.writeSplitVarint64(jspb.utils.split64Low, + jspb.utils.split64High); +}; + + +/** + * Writes a 8-bit unsigned integer to the buffer. Numbers outside the range + * [0,2^8) will be truncated. + * @param {number} value The value to write. + */ +jspb.BinaryEncoder.prototype.writeUint8 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= 0) && (value < 256)); + this.buffer_.push((value >>> 0) & 0xFF); +}; + + +/** + * Writes a 16-bit unsigned integer to the buffer. Numbers outside the + * range [0,2^16) will be truncated. + * @param {number} value The value to write. + */ +jspb.BinaryEncoder.prototype.writeUint16 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= 0) && (value < 65536)); + this.buffer_.push((value >>> 0) & 0xFF); + this.buffer_.push((value >>> 8) & 0xFF); +}; + + +/** + * Writes a 32-bit unsigned integer to the buffer. Numbers outside the + * range [0,2^32) will be truncated. + * @param {number} value The value to write. + */ +jspb.BinaryEncoder.prototype.writeUint32 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= 0) && + (value < jspb.BinaryConstants.TWO_TO_32)); + this.buffer_.push((value >>> 0) & 0xFF); + this.buffer_.push((value >>> 8) & 0xFF); + this.buffer_.push((value >>> 16) & 0xFF); + this.buffer_.push((value >>> 24) & 0xFF); +}; + + +/** + * Writes a 64-bit unsigned integer to the buffer. Numbers outside the + * range [0,2^64) will be truncated. + * @param {number} value The value to write. + */ +jspb.BinaryEncoder.prototype.writeUint64 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= 0) && + (value < jspb.BinaryConstants.TWO_TO_64)); + jspb.utils.splitUint64(value); + this.writeUint32(jspb.utils.split64Low); + this.writeUint32(jspb.utils.split64High); +}; + + +/** + * Writes a 8-bit integer to the buffer. Numbers outside the range + * [-2^7,2^7) will be truncated. + * @param {number} value The value to write. + */ +jspb.BinaryEncoder.prototype.writeInt8 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= -128) && (value < 128)); + this.buffer_.push((value >>> 0) & 0xFF); +}; + + +/** + * Writes a 16-bit integer to the buffer. Numbers outside the range + * [-2^15,2^15) will be truncated. + * @param {number} value The value to write. + */ +jspb.BinaryEncoder.prototype.writeInt16 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= -32768) && (value < 32768)); + this.buffer_.push((value >>> 0) & 0xFF); + this.buffer_.push((value >>> 8) & 0xFF); +}; + + +/** + * Writes a 32-bit integer to the buffer. Numbers outside the range + * [-2^31,2^31) will be truncated. + * @param {number} value The value to write. + */ +jspb.BinaryEncoder.prototype.writeInt32 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) && + (value < jspb.BinaryConstants.TWO_TO_31)); + this.buffer_.push((value >>> 0) & 0xFF); + this.buffer_.push((value >>> 8) & 0xFF); + this.buffer_.push((value >>> 16) & 0xFF); + this.buffer_.push((value >>> 24) & 0xFF); +}; + + +/** + * Writes a 64-bit integer to the buffer. Numbers outside the range + * [-2^63,2^63) will be truncated. + * @param {number} value The value to write. + */ +jspb.BinaryEncoder.prototype.writeInt64 = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) && + (value < jspb.BinaryConstants.TWO_TO_63)); + jspb.utils.splitInt64(value); + this.writeUint32(jspb.utils.split64Low); + this.writeUint32(jspb.utils.split64High); +}; + + +/** + * Writes a single-precision floating point value to the buffer. Numbers + * requiring more than 32 bits of precision will be truncated. + * @param {number} value The value to write. + */ +jspb.BinaryEncoder.prototype.writeFloat = function(value) { + goog.asserts.assert((value >= -jspb.BinaryConstants.FLOAT32_MAX) && + (value <= jspb.BinaryConstants.FLOAT32_MAX)); + jspb.utils.splitFloat32(value); + this.writeUint32(jspb.utils.split64Low); +}; + + +/** + * Writes a double-precision floating point value to the buffer. As this is + * the native format used by JavaScript, no precision will be lost. + * @param {number} value The value to write. + */ +jspb.BinaryEncoder.prototype.writeDouble = function(value) { + goog.asserts.assert((value >= -jspb.BinaryConstants.FLOAT64_MAX) && + (value <= jspb.BinaryConstants.FLOAT64_MAX)); + jspb.utils.splitFloat64(value); + this.writeUint32(jspb.utils.split64Low); + this.writeUint32(jspb.utils.split64High); +}; + + +/** + * Writes a boolean value to the buffer as a varint. + * @param {boolean} value The value to write. + */ +jspb.BinaryEncoder.prototype.writeBool = function(value) { + goog.asserts.assert(goog.isBoolean(value)); + this.buffer_.push(value ? 1 : 0); +}; + + +/** + * Writes an enum value to the buffer as a varint. + * @param {number} value The value to write. + */ +jspb.BinaryEncoder.prototype.writeEnum = function(value) { + goog.asserts.assert(value == Math.floor(value)); + goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) && + (value < jspb.BinaryConstants.TWO_TO_31)); + this.writeSignedVarint32(value); +}; + + +/** + * Writes an arbitrary byte array to the buffer. + * @param {!Uint8Array} bytes The array of bytes to write. + */ +jspb.BinaryEncoder.prototype.writeBytes = function(bytes) { + this.buffer_.push.apply(this.buffer_, bytes); +}; + + +/** + * Writes a 64-bit hash string (8 characters @ 8 bits of data each) to the + * buffer as a varint. + * @param {string} hash The hash to write. + */ +jspb.BinaryEncoder.prototype.writeVarintHash64 = function(hash) { + jspb.utils.splitHash64(hash); + this.writeSplitVarint64(jspb.utils.split64Low, + jspb.utils.split64High); +}; + + +/** + * Writes a 64-bit hash string (8 characters @ 8 bits of data each) to the + * buffer as a fixed64. + * @param {string} hash The hash to write. + */ +jspb.BinaryEncoder.prototype.writeFixedHash64 = function(hash) { + jspb.utils.splitHash64(hash); + this.writeUint32(jspb.utils.split64Low); + this.writeUint32(jspb.utils.split64High); +}; + + +/** + * Writes a UTF16 Javascript string to the buffer encoded as UTF8. + * TODO(aappleby): Add support for surrogate pairs, reject unpaired surrogates. + * @param {string} value The string to write. + * @return {number} The number of bytes used to encode the string. + */ +jspb.BinaryEncoder.prototype.writeString = function(value) { + var oldLength = this.buffer_.length; + + // UTF16 to UTF8 conversion loop swiped from goog.crypt.stringToUtf8ByteArray. + for (var i = 0; i < value.length; i++) { + var c = value.charCodeAt(i); + if (c < 128) { + this.buffer_.push(c); + } else if (c < 2048) { + this.buffer_.push((c >> 6) | 192); + this.buffer_.push((c & 63) | 128); + } else { + this.buffer_.push((c >> 12) | 224); + this.buffer_.push(((c >> 6) & 63) | 128); + this.buffer_.push((c & 63) | 128); + } + } + + var length = this.buffer_.length - oldLength; + return length; +}; diff --git a/js/binary/proto_test.js b/js/binary/proto_test.js index 3b4aa17fe..14d0f42e9 100644 --- a/js/binary/proto_test.js +++ b/js/binary/proto_test.js @@ -30,7 +30,9 @@ // Test suite is written using Jasmine -- see http://jasmine.github.io/ +goog.require('goog.crypt.base64'); goog.require('goog.testing.asserts'); +goog.require('jspb.Message'); // CommonJS-LoadFromFile: ../testbinary_pb proto.jspb.test goog.require('proto.jspb.test.ExtendsWithMessage'); @@ -38,9 +40,61 @@ goog.require('proto.jspb.test.ForeignEnum'); goog.require('proto.jspb.test.ForeignMessage'); goog.require('proto.jspb.test.TestAllTypes'); goog.require('proto.jspb.test.TestExtendable'); +goog.require('proto.jspb.test.extendOptionalBool'); +goog.require('proto.jspb.test.extendOptionalBytes'); +goog.require('proto.jspb.test.extendOptionalDouble'); +goog.require('proto.jspb.test.extendOptionalFixed32'); +goog.require('proto.jspb.test.extendOptionalFixed64'); +goog.require('proto.jspb.test.extendOptionalFloat'); +goog.require('proto.jspb.test.extendOptionalForeignEnum'); +goog.require('proto.jspb.test.extendOptionalInt32'); +goog.require('proto.jspb.test.extendOptionalInt64'); +goog.require('proto.jspb.test.extendOptionalSfixed32'); +goog.require('proto.jspb.test.extendOptionalSfixed64'); +goog.require('proto.jspb.test.extendOptionalSint32'); +goog.require('proto.jspb.test.extendOptionalSint64'); +goog.require('proto.jspb.test.extendOptionalString'); +goog.require('proto.jspb.test.extendOptionalUint32'); +goog.require('proto.jspb.test.extendOptionalUint64'); +goog.require('proto.jspb.test.extendPackedRepeatedBoolList'); +goog.require('proto.jspb.test.extendPackedRepeatedDoubleList'); +goog.require('proto.jspb.test.extendPackedRepeatedFixed32List'); +goog.require('proto.jspb.test.extendPackedRepeatedFixed64List'); +goog.require('proto.jspb.test.extendPackedRepeatedFloatList'); +goog.require('proto.jspb.test.extendPackedRepeatedForeignEnumList'); +goog.require('proto.jspb.test.extendPackedRepeatedInt32List'); +goog.require('proto.jspb.test.extendPackedRepeatedInt64List'); +goog.require('proto.jspb.test.extendPackedRepeatedSfixed32List'); +goog.require('proto.jspb.test.extendPackedRepeatedSfixed64List'); +goog.require('proto.jspb.test.extendPackedRepeatedSint32List'); +goog.require('proto.jspb.test.extendPackedRepeatedSint64List'); +goog.require('proto.jspb.test.extendPackedRepeatedUint32List'); +goog.require('proto.jspb.test.extendPackedRepeatedUint64List'); +goog.require('proto.jspb.test.extendRepeatedBoolList'); +goog.require('proto.jspb.test.extendRepeatedBytesList'); +goog.require('proto.jspb.test.extendRepeatedDoubleList'); +goog.require('proto.jspb.test.extendRepeatedFixed32List'); +goog.require('proto.jspb.test.extendRepeatedFixed64List'); +goog.require('proto.jspb.test.extendRepeatedFloatList'); +goog.require('proto.jspb.test.extendRepeatedForeignEnumList'); +goog.require('proto.jspb.test.extendRepeatedInt32List'); +goog.require('proto.jspb.test.extendRepeatedInt64List'); +goog.require('proto.jspb.test.extendRepeatedSfixed32List'); +goog.require('proto.jspb.test.extendRepeatedSfixed64List'); +goog.require('proto.jspb.test.extendRepeatedSint32List'); +goog.require('proto.jspb.test.extendRepeatedSint64List'); +goog.require('proto.jspb.test.extendRepeatedStringList'); +goog.require('proto.jspb.test.extendRepeatedUint32List'); +goog.require('proto.jspb.test.extendRepeatedUint64List'); + var suite = {}; +var BYTES = new Uint8Array([1, 2, 8, 9]); + +var BYTES_B64 = goog.crypt.base64.encodeByteArray(BYTES); + + /** * Helper: fill all fields on a TestAllTypes message. * @param {proto.jspb.test.TestAllTypes} msg @@ -62,7 +116,7 @@ function fillAllFields(msg) { msg.setOptionalDouble(-1.5); msg.setOptionalBool(true); msg.setOptionalString('hello world'); - msg.setOptionalBytes('bytes'); + msg.setOptionalBytes(BYTES); msg.setOptionalGroup(new proto.jspb.test.TestAllTypes.OptionalGroup()); msg.getOptionalGroup().setA(100); var submsg = new proto.jspb.test.ForeignMessage(); @@ -71,6 +125,7 @@ function fillAllFields(msg) { msg.setOptionalForeignEnum(proto.jspb.test.ForeignEnum.FOREIGN_FOO); msg.setOneofString('oneof'); + msg.setRepeatedInt32List([-42]); msg.setRepeatedInt64List([-0x7fffffff00000000]); msg.setRepeatedUint32List([0x80000000]); @@ -85,7 +140,7 @@ function fillAllFields(msg) { msg.setRepeatedDoubleList([-1.5]); msg.setRepeatedBoolList([true]); msg.setRepeatedStringList(['hello world']); - msg.setRepeatedBytesList(['bytes']); + msg.setRepeatedBytesList([BYTES, BYTES]); msg.setRepeatedGroupList([new proto.jspb.test.TestAllTypes.RepeatedGroup()]); msg.getRepeatedGroupList()[0].setA(100); submsg = new proto.jspb.test.ForeignMessage(); @@ -106,106 +161,115 @@ function fillAllFields(msg) { msg.setPackedRepeatedFloatList([1.5]); msg.setPackedRepeatedDoubleList([-1.5]); msg.setPackedRepeatedBoolList([true]); + } /** - * Helper: compare a bytes field to a string with codepoints 0--255. + * Helper: compare a bytes field to an expected value * @param {Uint8Array|string} arr - * @param {string} str + * @param {Uint8Array} expected * @return {boolean} */ -function bytesCompare(arr, str) { - if (arr.length != str.length) { +function bytesCompare(arr, expected) { + if (goog.isString(arr)) { + arr = goog.crypt.base64.decodeStringToUint8Array(arr); + } + if (arr.length != expected.length) { return false; } - if (typeof arr == 'string') { - for (var i = 0; i < arr.length; i++) { - if (arr.charCodeAt(i) != str.charCodeAt(i)) { - return false; - } + for (var i = 0; i < arr.length; i++) { + if (arr[i] != expected[i]) { + return false; } - return true; - } else { - for (var i = 0; i < arr.length; i++) { - if (arr[i] != str.charCodeAt(i)) { - return false; - } - } - return true; } + return true; } /** * Helper: verify contents of given TestAllTypes message as set by * fillAllFields(). - * @param {proto.jspb.test.TestAllTypes} msg + * @param {proto.jspb.test.TestAllTypes} original + * @param {proto.jspb.test.TestAllTypes} copy */ -function checkAllFields(msg) { - assertEquals(msg.getOptionalInt32(), -42); - assertEquals(msg.getOptionalInt64(), -0x7fffffff00000000); - assertEquals(msg.getOptionalUint32(), 0x80000000); - assertEquals(msg.getOptionalUint64(), 0xf000000000000000); - assertEquals(msg.getOptionalSint32(), -100); - assertEquals(msg.getOptionalSint64(), -0x8000000000000000); - assertEquals(msg.getOptionalFixed32(), 1234); - assertEquals(msg.getOptionalFixed64(), 0x1234567800000000); - assertEquals(msg.getOptionalSfixed32(), -1234); - assertEquals(msg.getOptionalSfixed64(), -0x1234567800000000); - assertEquals(msg.getOptionalFloat(), 1.5); - assertEquals(msg.getOptionalDouble(), -1.5); - assertEquals(msg.getOptionalBool(), true); - assertEquals(msg.getOptionalString(), 'hello world'); - assertEquals(true, bytesCompare(msg.getOptionalBytes(), 'bytes')); - assertEquals(msg.getOptionalGroup().getA(), 100); - assertEquals(msg.getOptionalForeignMessage().getC(), 16); - assertEquals(msg.getOptionalForeignEnum(), +function checkAllFields(original, copy) { + assertTrue(jspb.Message.equals(original, copy)); + + assertEquals(copy.getOptionalInt32(), -42); + assertEquals(copy.getOptionalInt64(), -0x7fffffff00000000); + assertEquals(copy.getOptionalUint32(), 0x80000000); + assertEquals(copy.getOptionalUint64(), 0xf000000000000000); + assertEquals(copy.getOptionalSint32(), -100); + assertEquals(copy.getOptionalSint64(), -0x8000000000000000); + assertEquals(copy.getOptionalFixed32(), 1234); + assertEquals(copy.getOptionalFixed64(), 0x1234567800000000); + assertEquals(copy.getOptionalSfixed32(), -1234); + assertEquals(copy.getOptionalSfixed64(), -0x1234567800000000); + assertEquals(copy.getOptionalFloat(), 1.5); + assertEquals(copy.getOptionalDouble(), -1.5); + assertEquals(copy.getOptionalBool(), true); + assertEquals(copy.getOptionalString(), 'hello world'); + assertEquals(true, bytesCompare(copy.getOptionalBytes(), BYTES)); + assertEquals(true, bytesCompare(copy.getOptionalBytes_asU8(), BYTES)); + assertEquals( + copy.getOptionalBytes_asB64(), goog.crypt.base64.encodeByteArray(BYTES)); + + assertEquals(copy.getOptionalGroup().getA(), 100); + assertEquals(copy.getOptionalForeignMessage().getC(), 16); + assertEquals(copy.getOptionalForeignEnum(), proto.jspb.test.ForeignEnum.FOREIGN_FOO); - assertEquals(msg.getOneofString(), 'oneof'); - assertEquals(msg.getOneofFieldCase(), + + + assertEquals(copy.getOneofString(), 'oneof'); + assertEquals(copy.getOneofFieldCase(), proto.jspb.test.TestAllTypes.OneofFieldCase.ONEOF_STRING); - assertElementsEquals(msg.getRepeatedInt32List(), [-42]); - assertElementsEquals(msg.getRepeatedInt64List(), [-0x7fffffff00000000]); - assertElementsEquals(msg.getRepeatedUint32List(), [0x80000000]); - assertElementsEquals(msg.getRepeatedUint64List(), [0xf000000000000000]); - assertElementsEquals(msg.getRepeatedSint32List(), [-100]); - assertElementsEquals(msg.getRepeatedSint64List(), [-0x8000000000000000]); - assertElementsEquals(msg.getRepeatedFixed32List(), [1234]); - assertElementsEquals(msg.getRepeatedFixed64List(), [0x1234567800000000]); - assertElementsEquals(msg.getRepeatedSfixed32List(), [-1234]); - assertElementsEquals(msg.getRepeatedSfixed64List(), [-0x1234567800000000]); - assertElementsEquals(msg.getRepeatedFloatList(), [1.5]); - assertElementsEquals(msg.getRepeatedDoubleList(), [-1.5]); - assertElementsEquals(msg.getRepeatedBoolList(), [true]); - assertElementsEquals(msg.getRepeatedStringList(), ['hello world']); - assertEquals(msg.getRepeatedBytesList().length, 1); - assertEquals(true, bytesCompare(msg.getRepeatedBytesList()[0], 'bytes')); - assertEquals(msg.getRepeatedGroupList().length, 1); - assertEquals(msg.getRepeatedGroupList()[0].getA(), 100); - assertEquals(msg.getRepeatedForeignMessageList().length, 1); - assertEquals(msg.getRepeatedForeignMessageList()[0].getC(), 1000); - assertElementsEquals(msg.getRepeatedForeignEnumList(), + assertElementsEquals(copy.getRepeatedInt32List(), [-42]); + assertElementsEquals(copy.getRepeatedInt64List(), [-0x7fffffff00000000]); + assertElementsEquals(copy.getRepeatedUint32List(), [0x80000000]); + assertElementsEquals(copy.getRepeatedUint64List(), [0xf000000000000000]); + assertElementsEquals(copy.getRepeatedSint32List(), [-100]); + assertElementsEquals(copy.getRepeatedSint64List(), [-0x8000000000000000]); + assertElementsEquals(copy.getRepeatedFixed32List(), [1234]); + assertElementsEquals(copy.getRepeatedFixed64List(), [0x1234567800000000]); + assertElementsEquals(copy.getRepeatedSfixed32List(), [-1234]); + assertElementsEquals(copy.getRepeatedSfixed64List(), [-0x1234567800000000]); + assertElementsEquals(copy.getRepeatedFloatList(), [1.5]); + assertElementsEquals(copy.getRepeatedDoubleList(), [-1.5]); + assertElementsEquals(copy.getRepeatedBoolList(), [true]); + assertElementsEquals(copy.getRepeatedStringList(), ['hello world']); + assertEquals(copy.getRepeatedBytesList().length, 2); + assertEquals(true, bytesCompare(copy.getRepeatedBytesList_asU8()[0], BYTES)); + assertEquals(true, bytesCompare(copy.getRepeatedBytesList()[0], BYTES)); + assertEquals(true, bytesCompare(copy.getRepeatedBytesList_asU8()[1], BYTES)); + assertEquals(copy.getRepeatedBytesList_asB64()[0], BYTES_B64); + assertEquals(copy.getRepeatedBytesList_asB64()[1], BYTES_B64); + assertEquals(copy.getRepeatedGroupList().length, 1); + assertEquals(copy.getRepeatedGroupList()[0].getA(), 100); + assertEquals(copy.getRepeatedForeignMessageList().length, 1); + assertEquals(copy.getRepeatedForeignMessageList()[0].getC(), 1000); + assertElementsEquals(copy.getRepeatedForeignEnumList(), [proto.jspb.test.ForeignEnum.FOREIGN_FOO]); - assertElementsEquals(msg.getPackedRepeatedInt32List(), [-42]); - assertElementsEquals(msg.getPackedRepeatedInt64List(), + assertElementsEquals(copy.getPackedRepeatedInt32List(), [-42]); + assertElementsEquals(copy.getPackedRepeatedInt64List(), [-0x7fffffff00000000]); - assertElementsEquals(msg.getPackedRepeatedUint32List(), [0x80000000]); - assertElementsEquals(msg.getPackedRepeatedUint64List(), [0xf000000000000000]); - assertElementsEquals(msg.getPackedRepeatedSint32List(), [-100]); - assertElementsEquals(msg.getPackedRepeatedSint64List(), + assertElementsEquals(copy.getPackedRepeatedUint32List(), [0x80000000]); + assertElementsEquals(copy.getPackedRepeatedUint64List(), + [0xf000000000000000]); + assertElementsEquals(copy.getPackedRepeatedSint32List(), [-100]); + assertElementsEquals(copy.getPackedRepeatedSint64List(), [-0x8000000000000000]); - assertElementsEquals(msg.getPackedRepeatedFixed32List(), [1234]); - assertElementsEquals(msg.getPackedRepeatedFixed64List(), + assertElementsEquals(copy.getPackedRepeatedFixed32List(), [1234]); + assertElementsEquals(copy.getPackedRepeatedFixed64List(), [0x1234567800000000]); - assertElementsEquals(msg.getPackedRepeatedSfixed32List(), [-1234]); - assertElementsEquals(msg.getPackedRepeatedSfixed64List(), + assertElementsEquals(copy.getPackedRepeatedSfixed32List(), [-1234]); + assertElementsEquals(copy.getPackedRepeatedSfixed64List(), [-0x1234567800000000]); - assertElementsEquals(msg.getPackedRepeatedFloatList(), [1.5]); - assertElementsEquals(msg.getPackedRepeatedDoubleList(), [-1.5]); - assertElementsEquals(msg.getPackedRepeatedBoolList(), [true]); + assertElementsEquals(copy.getPackedRepeatedFloatList(), [1.5]); + assertElementsEquals(copy.getPackedRepeatedDoubleList(), [-1.5]); + } @@ -242,14 +306,13 @@ function checkExtensions(msg) { msg.getExtension(proto.jspb.test.extendOptionalBool)); assertEquals('hello world', msg.getExtension(proto.jspb.test.extendOptionalString)); - assertEquals(true, - bytesCompare(msg.getExtension(proto.jspb.test.extendOptionalBytes), - 'bytes')); + assertEquals( + true, bytesCompare( + msg.getExtension(proto.jspb.test.extendOptionalBytes), BYTES)); assertEquals(16, msg.getExtension( proto.jspb.test.ExtendsWithMessage.optionalExtension).getFoo()); - assertEquals(proto.jspb.test.ForeignEnum.FOREIGN_FOO, - msg.getExtension(proto.jspb.test.extendOptionalForeignEnum)); + assertElementsEquals( msg.getExtension(proto.jspb.test.extendRepeatedInt32List), @@ -293,10 +356,10 @@ function checkExtensions(msg) { assertElementsEquals( msg.getExtension(proto.jspb.test.extendRepeatedStringList), ['hello world']); - assertEquals(true, + assertEquals( + true, bytesCompare( - msg.getExtension(proto.jspb.test.extendRepeatedBytesList)[0], - 'bytes')); + msg.getExtension(proto.jspb.test.extendRepeatedBytesList)[0], BYTES)); assertEquals(1000, msg.getExtension( proto.jspb.test.ExtendsWithMessage.repeatedExtensionList)[0] @@ -305,6 +368,7 @@ function checkExtensions(msg) { msg.getExtension(proto.jspb.test.extendRepeatedForeignEnumList), [proto.jspb.test.ForeignEnum.FOREIGN_FOO]); + assertElementsEquals( msg.getExtension(proto.jspb.test.extendPackedRepeatedInt32List), [-42]); @@ -347,6 +411,7 @@ function checkExtensions(msg) { assertElementsEquals( msg.getExtension(proto.jspb.test.extendPackedRepeatedForeignEnumList), [proto.jspb.test.ForeignEnum.FOREIGN_FOO]); + } @@ -360,9 +425,82 @@ describe('protoBinaryTest', function() { fillAllFields(msg); var encoded = msg.serializeBinary(); var decoded = proto.jspb.test.TestAllTypes.deserializeBinary(encoded); - checkAllFields(decoded); + checkAllFields(msg, decoded); }); + /** + * Test that base64 string and Uint8Array are interchangeable in bytes fields. + */ + it('testBytesFieldsGettersInterop', function() { + var msg = new proto.jspb.test.TestAllTypes(); + // Set from a base64 string and check all the getters work. + msg.setOptionalBytes(BYTES_B64); + assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + // Test binary serialize round trip doesn't break it. + msg = proto.jspb.test.TestAllTypes.deserializeBinary(msg.serializeBinary()); + assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + msg = new proto.jspb.test.TestAllTypes(); + // Set from a Uint8Array and check all the getters work. + msg.setOptionalBytes(BYTES); + assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + }); + + /** + * Test that bytes setters will receive result of any of the getters. + */ + it('testBytesFieldsSettersInterop', function() { + var msg = new proto.jspb.test.TestAllTypes(); + msg.setOptionalBytes(BYTES); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + msg.setOptionalBytes(msg.getOptionalBytes()); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + msg.setOptionalBytes(msg.getOptionalBytes_asB64()); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + msg.setOptionalBytes(msg.getOptionalBytes_asU8()); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + }); + + /** + * Test that bytes setters will receive result of any of the getters. + */ + it('testRepeatedBytesGetters', function() { + var msg = new proto.jspb.test.TestAllTypes(); + + function assertGetters() { + assertTrue(goog.isString(msg.getRepeatedBytesList_asB64()[0])); + assertTrue(goog.isString(msg.getRepeatedBytesList_asB64()[1])); + assertTrue(msg.getRepeatedBytesList_asU8()[0] instanceof Uint8Array); + assertTrue(msg.getRepeatedBytesList_asU8()[1] instanceof Uint8Array); + + assertTrue(bytesCompare(msg.getRepeatedBytesList()[0], BYTES)); + assertTrue(bytesCompare(msg.getRepeatedBytesList()[1], BYTES)); + assertTrue(bytesCompare(msg.getRepeatedBytesList_asB64()[0], BYTES)); + assertTrue(bytesCompare(msg.getRepeatedBytesList_asB64()[1], BYTES)); + assertTrue(bytesCompare(msg.getRepeatedBytesList_asU8()[0], BYTES)); + assertTrue(bytesCompare(msg.getRepeatedBytesList_asU8()[1], BYTES)); + } + + msg.setRepeatedBytesList([BYTES, BYTES]); + assertGetters(); + + msg.setRepeatedBytesList([BYTES_B64, BYTES_B64]); + assertGetters(); + + msg.setRepeatedBytesList(null); + assertEquals(0, msg.getRepeatedBytesList().length); + assertEquals(0, msg.getRepeatedBytesList_asB64().length); + assertEquals(0, msg.getRepeatedBytesList_asU8().length); + }); /** * Helper: fill all extension values. @@ -397,8 +535,7 @@ describe('protoBinaryTest', function() { proto.jspb.test.extendOptionalBool, true); msg.setExtension( proto.jspb.test.extendOptionalString, 'hello world'); - msg.setExtension( - proto.jspb.test.extendOptionalBytes, 'bytes'); + msg.setExtension(proto.jspb.test.extendOptionalBytes, BYTES); var submsg = new proto.jspb.test.ExtendsWithMessage(); submsg.setFoo(16); msg.setExtension( @@ -407,6 +544,7 @@ describe('protoBinaryTest', function() { proto.jspb.test.extendOptionalForeignEnum, proto.jspb.test.ForeignEnum.FOREIGN_FOO); + msg.setExtension( proto.jspb.test.extendRepeatedInt32List, [-42]); msg.setExtension( @@ -435,8 +573,7 @@ describe('protoBinaryTest', function() { proto.jspb.test.extendRepeatedBoolList, [true]); msg.setExtension( proto.jspb.test.extendRepeatedStringList, ['hello world']); - msg.setExtension( - proto.jspb.test.extendRepeatedBytesList, ['bytes']); + msg.setExtension(proto.jspb.test.extendRepeatedBytesList, [BYTES]); submsg = new proto.jspb.test.ExtendsWithMessage(); submsg.setFoo(1000); msg.setExtension( @@ -444,6 +581,7 @@ describe('protoBinaryTest', function() { msg.setExtension(proto.jspb.test.extendRepeatedForeignEnumList, [proto.jspb.test.ForeignEnum.FOREIGN_FOO]); + msg.setExtension( proto.jspb.test.extendPackedRepeatedInt32List, [-42]); msg.setExtension( @@ -473,6 +611,7 @@ describe('protoBinaryTest', function() { proto.jspb.test.extendPackedRepeatedBoolList, [true]); msg.setExtension(proto.jspb.test.extendPackedRepeatedForeignEnumList, [proto.jspb.test.ForeignEnum.FOREIGN_FOO]); + } diff --git a/js/binary/reader.js b/js/binary/reader.js index abcd1660d..15f90432d 100644 --- a/js/binary/reader.js +++ b/js/binary/reader.js @@ -180,7 +180,7 @@ jspb.BinaryReader.prototype.getCursor = function() { /** * Returns the raw buffer. - * @return {Uint8Array} The raw buffer. + * @return {?Uint8Array} The raw buffer. */ jspb.BinaryReader.prototype.getBuffer = function() { return this.decoder_.getBuffer(); @@ -592,8 +592,8 @@ jspb.BinaryReader.prototype.getFieldDecoder = function() { var start = this.decoder_.getCursor(); var end = start + length; - var innerDecoder = jspb.BinaryDecoder.alloc(this.decoder_.getBuffer(), - start, length); + var innerDecoder = + jspb.BinaryDecoder.alloc(this.decoder_.getBuffer(), start, length); this.decoder_.setCursor(end); return innerDecoder; }; @@ -869,7 +869,7 @@ jspb.BinaryReader.prototype.readString = function() { * Reads a length-prefixed block of bytes from the binary stream, or returns * null if the next field in the stream has an invalid length value. * - * @return {Uint8Array} The block of bytes. + * @return {!Uint8Array} The block of bytes. */ jspb.BinaryReader.prototype.readBytes = function() { goog.asserts.assert( diff --git a/js/binary/reader_test.js b/js/binary/reader_test.js index a6482610e..6f7e5d456 100644 --- a/js/binary/reader_test.js +++ b/js/binary/reader_test.js @@ -366,28 +366,28 @@ describe('binaryReaderTest', function() { var writer = new jspb.BinaryWriter(); var testSignedData = [ - '2730538252207801776', - '-2688470994844604560', - '3398529779486536359', - '3568577411627971000', - '272477188847484900', - '-6649058714086158188', - '-7695254765712060806', - '-4525541438037104029', - '-4993706538836508568', - '4990160321893729138' + '2730538252207801776', + '-2688470994844604560', + '3398529779486536359', + '3568577411627971000', + '272477188847484900', + '-6649058714086158188', + '-7695254765712060806', + '-4525541438037104029', + '-4993706538836508568', + '4990160321893729138' ]; var testUnsignedData = [ - '7822732630241694882', - '6753602971916687352', - '2399935075244442116', - '8724292567325338867', - '16948784802625696584', - '4136275908516066934', - '3575388346793700364', - '5167142028379259461', - '1557573948689737699', - '17100725280812548567' + '7822732630241694882', + '6753602971916687352', + '2399935075244442116', + '8724292567325338867', + '16948784802625696584', + '4136275908516066934', + '3575388346793700364', + '5167142028379259461', + '1557573948689737699', + '17100725280812548567' ]; for (var i = 0; i < testSignedData.length; i++) { @@ -535,7 +535,7 @@ describe('binaryReaderTest', function() { */ it('testNesting', function() { var writer = new jspb.BinaryWriter(); - var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); writer.writeInt32(1, 100); @@ -626,31 +626,15 @@ describe('binaryReaderTest', function() { writer.writeBytes(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); writer.writeString(4, 'The quick brown fox jumps over the lazy dog'); - // Write a group with a nested group inside. We use the internal - // .rawWriteVarint() to ensure the tested wire data is what we want, - // independently of any serialization logic. + // Write a group with a nested group inside. writer.writeInt32(5, sentinel); - // Start group, field 5. - writer.rawWriteVarint( - (5 << 3) + jspb.BinaryConstants.WireType.START_GROUP); - // Varint, field 42. - writer.rawWriteVarint( - (42 << 3) + jspb.BinaryConstants.WireType.VARINT); - // Varint data. - writer.rawWriteVarint(42); - // Start group, field 6. - writer.rawWriteVarint( - (6 << 3) + jspb.BinaryConstants.WireType.START_GROUP); - // Varint, field 84. - writer.rawWriteVarint( - (84 << 3) + jspb.BinaryConstants.WireType.VARINT); - writer.rawWriteVarint(42); - // End group, field 6. - writer.rawWriteVarint( - (6 << 3) + jspb.BinaryConstants.WireType.END_GROUP); - // End group, field 5. - writer.rawWriteVarint( - (5 << 3) + jspb.BinaryConstants.WireType.END_GROUP); + var dummyMessage = /** @type {!jspb.BinaryMessage} */({}); + writer.writeGroup(5, dummyMessage, function() { + writer.writeInt64(42, 42); + writer.writeGroup(6, dummyMessage, function() { + writer.writeInt64(84, 42); + }); + }); // Write final sentinel. writer.writeInt32(6, sentinel); diff --git a/js/binary/utils.js b/js/binary/utils.js index 926003894..514055531 100644 --- a/js/binary/utils.js +++ b/js/binary/utils.js @@ -567,6 +567,56 @@ jspb.utils.hash64ArrayToDecimalStrings = function(hashes, signed) { }; +/** + * Converts a signed or unsigned decimal string into its hash string + * representation. + * @param {string} dec + * @return {string} + */ +jspb.utils.decimalStringToHash64 = function(dec) { + goog.asserts.assert(dec.length > 0); + + // Check for minus sign. + var minus = false; + if (dec[0] === '-') { + minus = true; + dec = dec.slice(1); + } + + // Store result as a byte array. + var resultBytes = [0, 0, 0, 0, 0, 0, 0, 0]; + + // Set result to m*result + c. + function muladd(m, c) { + for (var i = 0; i < 8 && (m !== 1 || c > 0); i++) { + var r = m * resultBytes[i] + c; + resultBytes[i] = r & 0xFF; + c = r >>> 8; + } + } + + // Negate the result bits. + function neg() { + for (var i = 0; i < 8; i++) { + resultBytes[i] = (~resultBytes[i]) & 0xFF; + } + } + + // For each decimal digit, set result to 10*result + digit. + for (var i = 0; i < dec.length; i++) { + muladd(10, jspb.utils.DIGITS.indexOf(dec[i])); + } + + // If there's a minus sign, convert into two's complement. + if (minus) { + neg(); + muladd(1, 1); + } + + return String.fromCharCode.apply(null, resultBytes); +}; + + /** * Converts an 8-character hash string into its hexadecimal representation. * @param {string} hash @@ -838,63 +888,17 @@ jspb.utils.countDelimitedFields = function(buffer, start, end, field) { }; -/** - * Clones a scalar field. Pulling this out to a helper method saves us a few - * bytes of generated code. - * @param {Array} array - * @return {Array} - */ -jspb.utils.cloneRepeatedScalarField = function(array) { - return array ? array.slice() : null; -}; - - -/** - * Clones an array of messages using the provided cloner function. - * @param {Array.} messages - * @param {jspb.ClonerFunction} cloner - * @return {Array.} - */ -jspb.utils.cloneRepeatedMessageField = function(messages, cloner) { - if (messages === null) return null; - var result = []; - for (var i = 0; i < messages.length; i++) { - result.push(cloner(messages[i])); - } - return result; -}; - - -/** - * Clones an array of byte blobs. - * @param {Array.} blobs - * @return {Array.} - */ -jspb.utils.cloneRepeatedBlobField = function(blobs) { - if (blobs === null) return null; - var result = []; - for (var i = 0; i < blobs.length; i++) { - result.push(new Uint8Array(blobs[i])); - } - return result; -}; - - /** * String-ify bytes for text format. Should be optimized away in non-debug. * The returned string uses \xXX escapes for all values and is itself quoted. * [1, 31] serializes to '"\x01\x1f"'. * @param {jspb.ByteSource} byteSource The bytes to serialize. - * @param {boolean=} opt_stringIsRawBytes The string is interpreted as a series - * of raw bytes rather than base64 data. * @return {string} Stringified bytes for text format. */ -jspb.utils.debugBytesToTextFormat = function(byteSource, - opt_stringIsRawBytes) { +jspb.utils.debugBytesToTextFormat = function(byteSource) { var s = '"'; if (byteSource) { - var bytes = - jspb.utils.byteSourceToUint8Array(byteSource, opt_stringIsRawBytes); + var bytes = jspb.utils.byteSourceToUint8Array(byteSource); for (var i = 0; i < bytes.length; i++) { s += '\\x'; if (bytes[i] < 16) s += '0'; @@ -925,9 +929,8 @@ jspb.utils.debugScalarToTextFormat = function(scalar) { * exception. * @param {string} str * @return {!Uint8Array} - * @private */ -jspb.utils.stringToByteArray_ = function(str) { +jspb.utils.stringToByteArray = function(str) { var arr = new Uint8Array(str.length); for (var i = 0; i < str.length; i++) { var codepoint = str.charCodeAt(i); @@ -944,13 +947,10 @@ jspb.utils.stringToByteArray_ = function(str) { /** * Converts any type defined in jspb.ByteSource into a Uint8Array. * @param {!jspb.ByteSource} data - * @param {boolean=} opt_stringIsRawBytes Interpret a string as a series of raw - * bytes (encoded as codepoints 0--255 inclusive) rather than base64 data - * (default behavior). * @return {!Uint8Array} * @suppress {invalidCasts} */ -jspb.utils.byteSourceToUint8Array = function(data, opt_stringIsRawBytes) { +jspb.utils.byteSourceToUint8Array = function(data) { if (data.constructor === Uint8Array) { return /** @type {!Uint8Array} */(data); } @@ -967,11 +967,7 @@ jspb.utils.byteSourceToUint8Array = function(data, opt_stringIsRawBytes) { if (data.constructor === String) { data = /** @type {string} */(data); - if (opt_stringIsRawBytes) { - return jspb.utils.stringToByteArray_(data); - } else { - return goog.crypt.base64.decodeStringToUint8Array(data); - } + return goog.crypt.base64.decodeStringToUint8Array(data); } goog.asserts.fail('Type not convertible to Uint8Array.'); diff --git a/js/binary/utils_test.js b/js/binary/utils_test.js index 5c330791a..d27e5ea2c 100644 --- a/js/binary/utils_test.js +++ b/js/binary/utils_test.js @@ -197,6 +197,41 @@ describe('binaryUtilsTest', function() { assertEquals('123456789123456789', result[2]); }); + /* + * Going from decimal strings to hash strings should be lossless. + */ + it('testDecimalToHashConversion', function() { + var result; + var convert = jspb.utils.decimalStringToHash64; + + result = convert('0'); + assertEquals(String.fromCharCode.apply(null, + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), result); + + result = convert('-1'); + assertEquals(String.fromCharCode.apply(null, + [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), result); + + result = convert('18446744073709551615'); + assertEquals(String.fromCharCode.apply(null, + [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), result); + + result = convert('9223372036854775808'); + assertEquals(String.fromCharCode.apply(null, + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), result); + + result = convert('-9223372036854775808'); + assertEquals(String.fromCharCode.apply(null, + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]), result); + + result = convert('123456789123456789'); + assertEquals(String.fromCharCode.apply(null, + [0x15, 0x5F, 0xD0, 0xAC, 0x4B, 0x9B, 0xB6, 0x01]), result); + + result = convert('-123456789123456789'); + assertEquals(String.fromCharCode.apply(null, + [0xEB, 0xA0, 0x2F, 0x53, 0xB4, 0x64, 0x49, 0xFE]), result); + }); /** * Going from hash strings to hex strings should be lossless. @@ -310,7 +345,7 @@ describe('binaryUtilsTest', function() { // NaN. jspb.utils.splitFloat32(NaN); if (!isNaN(jspb.utils.joinFloat32(jspb.utils.split64Low, - jspb.utils.split64High))) { + jspb.utils.split64High))) { throw 'fail!'; } @@ -324,7 +359,7 @@ describe('binaryUtilsTest', function() { if (opt_bits != jspb.utils.split64Low) throw 'fail!'; } if (truncate(x) != jspb.utils.joinFloat32(jspb.utils.split64Low, - jspb.utils.split64High)) { + jspb.utils.split64High)) { throw 'fail!'; } } @@ -376,7 +411,7 @@ describe('binaryUtilsTest', function() { // NaN. jspb.utils.splitFloat64(NaN); if (!isNaN(jspb.utils.joinFloat64(jspb.utils.split64Low, - jspb.utils.split64High))) { + jspb.utils.split64High))) { throw 'fail!'; } @@ -394,7 +429,7 @@ describe('binaryUtilsTest', function() { if (opt_lowBits != jspb.utils.split64Low) throw 'fail!'; } if (x != jspb.utils.joinFloat64(jspb.utils.split64Low, - jspb.utils.split64High)) { + jspb.utils.split64High)) { throw 'fail!'; } } @@ -439,16 +474,20 @@ describe('binaryUtilsTest', function() { * Tests counting packed varints. */ it('testCountVarints', function() { - var writer = new jspb.BinaryWriter(); - - var count = 0; + var values = []; for (var i = 1; i < 1000000000; i *= 1.1) { - writer.rawWriteVarint(Math.floor(i)); - count++; + values.push(Math.floor(i)); } + var writer = new jspb.BinaryWriter(); + writer.writePackedUint64(1, values); + var buffer = new Uint8Array(writer.getResultBuffer()); - assertEquals(count, jspb.utils.countVarints(buffer, 0, buffer.length)); + + // We should have two more varints than we started with - one for the field + // tag, one for the packed length. + assertEquals(values.length + 2, + jspb.utils.countVarints(buffer, 0, buffer.length)); }); @@ -625,8 +664,5 @@ describe('binaryUtilsTest', function() { // Converting base64-encoded strings into Uint8Arrays should work. check(convert(sourceBase64)); - - // Converting binary-data strings into Uint8Arrays should work. - check(convert(sourceString, /* opt_stringIsRawBytes = */ true)); }); }); diff --git a/js/binary/writer.js b/js/binary/writer.js index a18494575..be4478ee0 100644 --- a/js/binary/writer.js +++ b/js/binary/writer.js @@ -60,12 +60,11 @@ goog.provide('jspb.BinaryWriter'); goog.require('goog.asserts'); goog.require('goog.crypt.base64'); goog.require('jspb.BinaryConstants'); +goog.require('jspb.BinaryEncoder'); goog.require('jspb.arith.Int64'); goog.require('jspb.arith.UInt64'); goog.require('jspb.utils'); -goog.forwardDeclare('jspb.Message'); - /** @@ -84,52 +83,31 @@ jspb.BinaryWriter = function() { this.blocks_ = []; /** - * Total number of bytes in the blocks_ array. Does _not_ include the temp - * buffer. + * Total number of bytes in the blocks_ array. Does _not_ include bytes in + * the encoder below. * @private {number} */ this.totalLength_ = 0; /** - * Temporary buffer holding a message that we're still serializing. When we - * get to a stopping point (either the start of a new submessage, or when we - * need to append a raw Uint8Array), the temp buffer will be added to the - * block array above and a new temp buffer will be created. - * @private {!Array.} + * Binary encoder holding pieces of a message that we're still serializing. + * When we get to a stopping point (either the start of a new submessage, or + * when we need to append a raw Uint8Array), the encoder's buffer will be + * added to the block array above and the encoder will be reset. + * @private {!jspb.BinaryEncoder} */ - this.temp_ = []; + this.encoder_ = new jspb.BinaryEncoder(); /** * A stack of bookmarks containing the parent blocks for each message started * via beginSubMessage(), needed as bookkeeping for endSubMessage(). * TODO(aappleby): Deprecated, users should be calling writeMessage(). - * @private {!Array.} + * @private {!Array.>} */ this.bookmarks_ = []; }; -/** - * @typedef {{block: !Array., length: number}} - * @private - */ -jspb.BinaryWriter.Bookmark_; - - -/** - * Saves the current temp buffer in the blocks_ array and starts a new one. - * @return {!Array.} Returns a reference to the old temp buffer. - * @private - */ -jspb.BinaryWriter.prototype.saveTempBuffer_ = function() { - var oldTemp = this.temp_; - this.blocks_.push(this.temp_); - this.totalLength_ += this.temp_.length; - this.temp_ = []; - return oldTemp; -}; - - /** * Append a typed array of bytes onto the buffer. * @@ -137,63 +115,77 @@ jspb.BinaryWriter.prototype.saveTempBuffer_ = function() { * @private */ jspb.BinaryWriter.prototype.appendUint8Array_ = function(arr) { - if (this.temp_.length) { - this.saveTempBuffer_(); - } + var temp = this.encoder_.end(); + this.blocks_.push(temp); this.blocks_.push(arr); - this.totalLength_ += arr.length; + this.totalLength_ += temp.length + arr.length; }; /** - * Append an untyped array of bytes onto the buffer. - * - * @param {!Array.} arr The byte array to append. - * @private - */ -jspb.BinaryWriter.prototype.appendArray_ = function(arr) { - if (this.temp_.length) { - this.saveTempBuffer_(); - } - this.temp_ = arr; -}; - - -/** - * Begins a length-delimited section by writing the field header to the current - * temp buffer and then saving it in the block array. Returns the saved block, - * which we will append the length to in endDelimited_ below. + * Begins a new message by writing the field header and returning a bookmark + * which we will use to patch in the message length to in endDelimited_ below. * @param {number} field - * @return {!jspb.BinaryWriter.Bookmark_} + * @return {!Array.} * @private */ jspb.BinaryWriter.prototype.beginDelimited_ = function(field) { - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); - return {block: this.saveTempBuffer_(), length: this.totalLength_}; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); + var bookmark = this.encoder_.end(); + this.blocks_.push(bookmark); + this.totalLength_ += bookmark.length; + bookmark.push(this.totalLength_); + return bookmark; }; /** - * Ends a length-delimited block by encoding the _change_ in length of the - * buffer to the parent block and adds the number of bytes needed to encode - * that length to the total byte length. Note that 'parentLength' _must_ be the - * total length _after_ the field header was written in beginDelimited_ above. - * @param {!jspb.BinaryWriter.Bookmark_} bookmark + * Ends a message by encoding the _change_ in length of the buffer to the + * parent block and adds the number of bytes needed to encode that length to + * the total byte length. + * @param {!Array.} bookmark * @private */ jspb.BinaryWriter.prototype.endDelimited_ = function(bookmark) { - var messageLength = this.totalLength_ + this.temp_.length - bookmark.length; + var oldLength = bookmark.pop(); + var messageLength = this.totalLength_ + this.encoder_.length() - oldLength; goog.asserts.assert(messageLength >= 0); - var bytes = 1; while (messageLength > 127) { - bookmark.block.push((messageLength & 0x7f) | 0x80); + bookmark.push((messageLength & 0x7f) | 0x80); messageLength = messageLength >>> 7; - bytes++; + this.totalLength_++; } - bookmark.block.push(messageLength); - this.totalLength_ += bytes; + bookmark.push(messageLength); + this.totalLength_++; +}; + + +/** + * Writes a pre-serialized message to the buffer. + * @param {!Uint8Array} bytes The array of bytes to write. + * @param {number} start The start of the range to write. + * @param {number} end The end of the range to write. + */ +jspb.BinaryWriter.prototype.writeSerializedMessage = function( + bytes, start, end) { + this.appendUint8Array_(bytes.subarray(start, end)); +}; + + +/** + * Writes a pre-serialized message to the buffer if the message and endpoints + * are non-null. + * @param {?Uint8Array} bytes The array of bytes to write. + * @param {?number} start The start of the range to write. + * @param {?number} end The end of the range to write. + */ +jspb.BinaryWriter.prototype.maybeWriteSerializedMessage = function( + bytes, start, end) { + if (bytes != null && start != null && end != null) { + this.writeSerializedMessage(bytes, start, end); + } }; @@ -202,7 +194,7 @@ jspb.BinaryWriter.prototype.endDelimited_ = function(bookmark) { */ jspb.BinaryWriter.prototype.reset = function() { this.blocks_ = []; - this.temp_ = []; + this.encoder_.end(); this.totalLength_ = 0; this.bookmarks_ = []; }; @@ -215,7 +207,7 @@ jspb.BinaryWriter.prototype.reset = function() { jspb.BinaryWriter.prototype.getResultBuffer = function() { goog.asserts.assert(this.bookmarks_.length == 0); - var flat = new Uint8Array(this.totalLength_ + this.temp_.length); + var flat = new Uint8Array(this.totalLength_ + this.encoder_.length()); var blocks = this.blocks_; var blockCount = blocks.length; @@ -227,8 +219,9 @@ jspb.BinaryWriter.prototype.getResultBuffer = function() { offset += block.length; } - flat.set(this.temp_, offset); - offset += this.temp_.length; + var tail = this.encoder_.end(); + flat.set(tail, offset); + offset += tail.length; // Post condition: `flattened` must have had every byte written. goog.asserts.assert(offset == flat.length); @@ -236,7 +229,6 @@ jspb.BinaryWriter.prototype.getResultBuffer = function() { // Replace our block list with the flattened block, which lets GC reclaim // the temp blocks sooner. this.blocks_ = [flat]; - this.temp_ = []; return flat; }; @@ -272,331 +264,6 @@ jspb.BinaryWriter.prototype.endSubMessage = function() { }; -/** - * Encodes a 32-bit unsigned integer into its wire-format varint representation - * and stores it in the buffer. - * @param {number} value The integer to convert. - */ -jspb.BinaryWriter.prototype.rawWriteUnsignedVarint32 = function(value) { - goog.asserts.assert(value == Math.floor(value)); - - while (value > 127) { - this.temp_.push((value & 0x7f) | 0x80); - value = value >>> 7; - } - - this.temp_.push(value); -}; - - -/** - * Encodes a 32-bit signed integer into its wire-format varint representation - * and stores it in the buffer. - * @param {number} value The integer to convert. - */ -jspb.BinaryWriter.prototype.rawWriteSignedVarint32 = function(value) { - goog.asserts.assert(value == Math.floor(value)); - if (value >= 0) { - this.rawWriteUnsignedVarint32(value); - return; - } - - // Write nine bytes with a _signed_ right shift so we preserve the sign bit. - for (var i = 0; i < 9; i++) { - this.temp_.push((value & 0x7f) | 0x80); - value = value >> 7; - } - - // The above loop writes out 63 bits, so the last byte is always the sign bit - // which is always set for negative numbers. - this.temp_.push(1); -}; - - -/** - * Encodes an unsigned 64-bit integer in 32:32 split representation into its - * wire-format varint representation and stores it in the buffer. - * @param {number} lowBits The low 32 bits of the int. - * @param {number} highBits The high 32 bits of the int. - */ -jspb.BinaryWriter.prototype.rawWriteSplitVarint = - function(lowBits, highBits) { - // Break the binary representation into chunks of 7 bits, set the 8th bit - // in each chunk if it's not the final chunk, and append to the result. - while (highBits > 0 || lowBits > 127) { - this.temp_.push((lowBits & 0x7f) | 0x80); - lowBits = ((lowBits >>> 7) | (highBits << 25)) >>> 0; - highBits = highBits >>> 7; - } - this.temp_.push(lowBits); -}; - - -/** - * Encodes a JavaScript integer into its wire-format varint representation and - * stores it in the buffer. Due to the way the varint encoding works this - * behaves correctly for both signed and unsigned integers, though integers - * that are not representable in 64 bits will still be truncated. - * @param {number} value The integer to convert. - */ -jspb.BinaryWriter.prototype.rawWriteVarint = function(value) { - goog.asserts.assert(value == Math.floor(value)); - jspb.utils.splitInt64(value); - this.rawWriteSplitVarint(jspb.utils.split64Low, - jspb.utils.split64High); -}; - - -/** - * Encodes a jspb.arith.{Int64,UInt64} instance into its wire-format - * varint representation and stores it in the buffer. Due to the way the varint - * encoding works this behaves correctly for both signed and unsigned integers, - * though integers that are not representable in 64 bits will still be - * truncated. - * @param {jspb.arith.Int64|jspb.arith.UInt64} value - */ -jspb.BinaryWriter.prototype.rawWriteVarintFromNum = function(value) { - this.rawWriteSplitVarint(value.lo, value.hi); -}; - - -/** - * Encodes a JavaScript integer into its wire-format, zigzag-encoded varint - * representation and stores it in the buffer. - * @param {number} value The integer to convert. - */ -jspb.BinaryWriter.prototype.rawWriteZigzagVarint32 = function(value) { - goog.asserts.assert(value == Math.floor(value)); - this.rawWriteUnsignedVarint32(((value << 1) ^ (value >> 31)) >>> 0); -}; - - -/** - * Encodes a JavaScript integer into its wire-format, zigzag-encoded varint - * representation and stores it in the buffer. Integers not representable in 64 - * bits will be truncated. - * @param {number} value The integer to convert. - */ -jspb.BinaryWriter.prototype.rawWriteZigzagVarint = function(value) { - goog.asserts.assert(value == Math.floor(value)); - jspb.utils.splitZigzag64(value); - this.rawWriteSplitVarint(jspb.utils.split64Low, - jspb.utils.split64High); -}; - - -/** - * Writes a raw 8-bit unsigned integer to the buffer. Numbers outside the range - * [0,2^8) will be truncated. - * @param {number} value The value to write. - */ -jspb.BinaryWriter.prototype.rawWriteUint8 = function(value) { - goog.asserts.assert(value == Math.floor(value)); - goog.asserts.assert((value >= 0) && (value < 256)); - this.temp_.push((value >>> 0) & 0xFF); -}; - - -/** - * Writes a raw 16-bit unsigned integer to the buffer. Numbers outside the - * range [0,2^16) will be truncated. - * @param {number} value The value to write. - */ -jspb.BinaryWriter.prototype.rawWriteUint16 = function(value) { - goog.asserts.assert(value == Math.floor(value)); - goog.asserts.assert((value >= 0) && (value < 65536)); - this.temp_.push((value >>> 0) & 0xFF); - this.temp_.push((value >>> 8) & 0xFF); -}; - - -/** - * Writes a raw 32-bit unsigned integer to the buffer. Numbers outside the - * range [0,2^32) will be truncated. - * @param {number} value The value to write. - */ -jspb.BinaryWriter.prototype.rawWriteUint32 = function(value) { - goog.asserts.assert(value == Math.floor(value)); - goog.asserts.assert((value >= 0) && - (value < jspb.BinaryConstants.TWO_TO_32)); - this.temp_.push((value >>> 0) & 0xFF); - this.temp_.push((value >>> 8) & 0xFF); - this.temp_.push((value >>> 16) & 0xFF); - this.temp_.push((value >>> 24) & 0xFF); -}; - - -/** - * Writes a raw 64-bit unsigned integer to the buffer. Numbers outside the - * range [0,2^64) will be truncated. - * @param {number} value The value to write. - */ -jspb.BinaryWriter.prototype.rawWriteUint64 = function(value) { - goog.asserts.assert(value == Math.floor(value)); - goog.asserts.assert((value >= 0) && - (value < jspb.BinaryConstants.TWO_TO_64)); - jspb.utils.splitUint64(value); - this.rawWriteUint32(jspb.utils.split64Low); - this.rawWriteUint32(jspb.utils.split64High); -}; - - -/** - * Writes a raw 8-bit integer to the buffer. Numbers outside the range - * [-2^7,2^7) will be truncated. - * @param {number} value The value to write. - */ -jspb.BinaryWriter.prototype.rawWriteInt8 = function(value) { - goog.asserts.assert(value == Math.floor(value)); - goog.asserts.assert((value >= -128) && (value < 128)); - this.temp_.push((value >>> 0) & 0xFF); -}; - - -/** - * Writes a raw 16-bit integer to the buffer. Numbers outside the range - * [-2^15,2^15) will be truncated. - * @param {number} value The value to write. - */ -jspb.BinaryWriter.prototype.rawWriteInt16 = function(value) { - goog.asserts.assert(value == Math.floor(value)); - goog.asserts.assert((value >= -32768) && (value < 32768)); - this.temp_.push((value >>> 0) & 0xFF); - this.temp_.push((value >>> 8) & 0xFF); -}; - - -/** - * Writes a raw 32-bit integer to the buffer. Numbers outside the range - * [-2^31,2^31) will be truncated. - * @param {number} value The value to write. - */ -jspb.BinaryWriter.prototype.rawWriteInt32 = function(value) { - goog.asserts.assert(value == Math.floor(value)); - goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) && - (value < jspb.BinaryConstants.TWO_TO_31)); - this.temp_.push((value >>> 0) & 0xFF); - this.temp_.push((value >>> 8) & 0xFF); - this.temp_.push((value >>> 16) & 0xFF); - this.temp_.push((value >>> 24) & 0xFF); -}; - - -/** - * Writes a raw 64-bit integer to the buffer. Numbers outside the range - * [-2^63,2^63) will be truncated. - * @param {number} value The value to write. - */ -jspb.BinaryWriter.prototype.rawWriteInt64 = function(value) { - goog.asserts.assert(value == Math.floor(value)); - goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) && - (value < jspb.BinaryConstants.TWO_TO_63)); - jspb.utils.splitInt64(value); - this.rawWriteUint32(jspb.utils.split64Low); - this.rawWriteUint32(jspb.utils.split64High); -}; - - -/** - * Writes a raw single-precision floating point value to the buffer. Numbers - * requiring more than 32 bits of precision will be truncated. - * @param {number} value The value to write. - */ -jspb.BinaryWriter.prototype.rawWriteFloat = function(value) { - jspb.utils.splitFloat32(value); - this.rawWriteUint32(jspb.utils.split64Low); -}; - - -/** - * Writes a raw double-precision floating point value to the buffer. As this is - * the native format used by JavaScript, no precision will be lost. - * @param {number} value The value to write. - */ -jspb.BinaryWriter.prototype.rawWriteDouble = function(value) { - jspb.utils.splitFloat64(value); - this.rawWriteUint32(jspb.utils.split64Low); - this.rawWriteUint32(jspb.utils.split64High); -}; - - -/** - * Writes a raw boolean value to the buffer as a varint. - * @param {boolean} value The value to write. - */ -jspb.BinaryWriter.prototype.rawWriteBool = function(value) { - goog.asserts.assert(goog.isBoolean(value)); - this.temp_.push(~~value); -}; - - -/** - * Writes an raw enum value to the buffer as a varint. - * @param {number} value The value to write. - */ -jspb.BinaryWriter.prototype.rawWriteEnum = function(value) { - goog.asserts.assert(value == Math.floor(value)); - goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) && - (value < jspb.BinaryConstants.TWO_TO_31)); - this.rawWriteSignedVarint32(value); -}; - - -/** - * Writes a raw string value to the buffer. - * @param {string} string The string to write. - */ -jspb.BinaryWriter.prototype.rawWriteUtf8String = function(string) { - for (var i = 0; i < string.length; i++) { - this.temp_.push(string.charCodeAt(i)); - } -}; - - -/** - * Writes an arbitrary raw byte array to the buffer. - * @param {!Uint8Array} bytes The array of bytes to write. - */ -jspb.BinaryWriter.prototype.rawWriteBytes = function(bytes) { - this.appendUint8Array_(bytes); -}; - - -/** - * Writes an arbitrary raw byte array to the buffer. - * @param {!Uint8Array} bytes The array of bytes to write. - * @param {number} start The start of the range to write. - * @param {number} end The end of the range to write. - */ -jspb.BinaryWriter.prototype.rawWriteByteRange = function(bytes, start, end) { - this.appendUint8Array_(bytes.subarray(start, end)); -}; - - -/** - * Writes a 64-bit hash string (8 characters @ 8 bits of data each) to the - * buffer as a varint. - * @param {string} hash The hash to write. - */ -jspb.BinaryWriter.prototype.rawWriteVarintHash64 = function(hash) { - jspb.utils.splitHash64(hash); - this.rawWriteSplitVarint(jspb.utils.split64Low, - jspb.utils.split64High); -}; - - -/** - * Writes a 64-bit hash string (8 characters @ 8 bits of data each) to the - * buffer as a fixed64. - * @param {string} hash The hash to write. - */ -jspb.BinaryWriter.prototype.rawWriteFixedHash64 = function(hash) { - jspb.utils.splitHash64(hash); - this.rawWriteUint32(jspb.utils.split64Low); - this.rawWriteUint32(jspb.utils.split64High); -}; - - /** * Encodes a (field number, wire type) tuple into a wire-format field header * and stores it in the buffer as a varint. @@ -605,11 +272,11 @@ jspb.BinaryWriter.prototype.rawWriteFixedHash64 = function(hash) { * protocol buffer documentation. * @private */ -jspb.BinaryWriter.prototype.rawWriteFieldHeader_ = +jspb.BinaryWriter.prototype.writeFieldHeader_ = function(field, wireType) { goog.asserts.assert(field >= 1 && field == Math.floor(field)); var x = field * 8 + wireType; - this.rawWriteUnsignedVarint32(x); + this.encoder_.writeUnsignedVarint32(x); }; @@ -697,8 +364,8 @@ jspb.BinaryWriter.prototype.writeAny = function(fieldType, field, value) { */ jspb.BinaryWriter.prototype.writeUnsignedVarint32_ = function(field, value) { if (value == null) return; - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); - this.rawWriteSignedVarint32(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); + this.encoder_.writeUnsignedVarint32(value); }; @@ -710,8 +377,8 @@ jspb.BinaryWriter.prototype.writeUnsignedVarint32_ = function(field, value) { */ jspb.BinaryWriter.prototype.writeSignedVarint32_ = function(field, value) { if (value == null) return; - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); - this.rawWriteSignedVarint32(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); + this.encoder_.writeSignedVarint32(value); }; @@ -721,10 +388,23 @@ jspb.BinaryWriter.prototype.writeSignedVarint32_ = function(field, value) { * @param {number?} value The value to write. * @private */ -jspb.BinaryWriter.prototype.writeVarint_ = function(field, value) { +jspb.BinaryWriter.prototype.writeUnsignedVarint64_ = function(field, value) { if (value == null) return; - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); - this.rawWriteVarint(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); + this.encoder_.writeUnsignedVarint64(value); +}; + + +/** + * Writes a varint field to the buffer without range checking. + * @param {number} field The field number. + * @param {number?} value The value to write. + * @private + */ +jspb.BinaryWriter.prototype.writeSignedVarint64_ = function(field, value) { + if (value == null) return; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); + this.encoder_.writeSignedVarint64(value); }; @@ -736,8 +416,8 @@ jspb.BinaryWriter.prototype.writeVarint_ = function(field, value) { */ jspb.BinaryWriter.prototype.writeZigzagVarint32_ = function(field, value) { if (value == null) return; - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); - this.rawWriteZigzagVarint32(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); + this.encoder_.writeZigzagVarint32(value); }; @@ -747,10 +427,10 @@ jspb.BinaryWriter.prototype.writeZigzagVarint32_ = function(field, value) { * @param {number?} value The value to write. * @private */ -jspb.BinaryWriter.prototype.writeZigzagVarint_ = function(field, value) { +jspb.BinaryWriter.prototype.writeZigzagVarint64_ = function(field, value) { if (value == null) return; - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); - this.rawWriteZigzagVarint(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); + this.encoder_.writeZigzagVarint64(value); }; @@ -793,7 +473,7 @@ jspb.BinaryWriter.prototype.writeInt64 = function(field, value) { if (value == null) return; goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) && (value < jspb.BinaryConstants.TWO_TO_63)); - this.writeVarint_(field, value); + this.writeSignedVarint64_(field, value); }; @@ -805,8 +485,8 @@ jspb.BinaryWriter.prototype.writeInt64 = function(field, value) { jspb.BinaryWriter.prototype.writeInt64String = function(field, value) { if (value == null) return; var num = jspb.arith.Int64.fromString(value); - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); - this.rawWriteVarintFromNum(num); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); + this.encoder_.writeSplitVarint64(num.lo, num.hi); }; @@ -849,7 +529,7 @@ jspb.BinaryWriter.prototype.writeUint64 = function(field, value) { if (value == null) return; goog.asserts.assert((value >= 0) && (value < jspb.BinaryConstants.TWO_TO_64)); - this.writeVarint_(field, value); + this.writeUnsignedVarint64_(field, value); }; @@ -861,8 +541,8 @@ jspb.BinaryWriter.prototype.writeUint64 = function(field, value) { jspb.BinaryWriter.prototype.writeUint64String = function(field, value) { if (value == null) return; var num = jspb.arith.UInt64.fromString(value); - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); - this.rawWriteVarintFromNum(num); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); + this.encoder_.writeSplitVarint64(num.lo, num.hi); }; @@ -890,7 +570,7 @@ jspb.BinaryWriter.prototype.writeSint64 = function(field, value) { if (value == null) return; goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) && (value < jspb.BinaryConstants.TWO_TO_63)); - this.writeZigzagVarint_(field, value); + this.writeZigzagVarint64_(field, value); }; @@ -904,8 +584,8 @@ jspb.BinaryWriter.prototype.writeFixed32 = function(field, value) { if (value == null) return; goog.asserts.assert((value >= 0) && (value < jspb.BinaryConstants.TWO_TO_32)); - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED32); - this.rawWriteUint32(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED32); + this.encoder_.writeUint32(value); }; @@ -919,8 +599,8 @@ jspb.BinaryWriter.prototype.writeFixed64 = function(field, value) { if (value == null) return; goog.asserts.assert((value >= 0) && (value < jspb.BinaryConstants.TWO_TO_64)); - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64); - this.rawWriteUint64(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64); + this.encoder_.writeUint64(value); }; @@ -934,8 +614,8 @@ jspb.BinaryWriter.prototype.writeSfixed32 = function(field, value) { if (value == null) return; goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) && (value < jspb.BinaryConstants.TWO_TO_31)); - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED32); - this.rawWriteInt32(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED32); + this.encoder_.writeInt32(value); }; @@ -949,8 +629,8 @@ jspb.BinaryWriter.prototype.writeSfixed64 = function(field, value) { if (value == null) return; goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) && (value < jspb.BinaryConstants.TWO_TO_63)); - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64); - this.rawWriteInt64(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64); + this.encoder_.writeInt64(value); }; @@ -962,8 +642,8 @@ jspb.BinaryWriter.prototype.writeSfixed64 = function(field, value) { */ jspb.BinaryWriter.prototype.writeFloat = function(field, value) { if (value == null) return; - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED32); - this.rawWriteFloat(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED32); + this.encoder_.writeFloat(value); }; @@ -975,8 +655,8 @@ jspb.BinaryWriter.prototype.writeFloat = function(field, value) { */ jspb.BinaryWriter.prototype.writeDouble = function(field, value) { if (value == null) return; - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64); - this.rawWriteDouble(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64); + this.encoder_.writeDouble(value); }; @@ -988,8 +668,8 @@ jspb.BinaryWriter.prototype.writeDouble = function(field, value) { jspb.BinaryWriter.prototype.writeBool = function(field, value) { if (value == null) return; goog.asserts.assert(goog.isBoolean(value)); - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); - this.temp_.push(~~value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); + this.encoder_.writeBool(value); }; @@ -1002,8 +682,8 @@ jspb.BinaryWriter.prototype.writeEnum = function(field, value) { if (value == null) return; goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) && (value < jspb.BinaryConstants.TWO_TO_31)); - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); - this.rawWriteSignedVarint32(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); + this.encoder_.writeSignedVarint32(value); }; @@ -1014,99 +694,41 @@ jspb.BinaryWriter.prototype.writeEnum = function(field, value) { */ jspb.BinaryWriter.prototype.writeString = function(field, value) { if (value == null) return; - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); - - // Conversion loop swiped from goog.crypt.stringToUtf8ByteArray. Note that - // 'bytes' will be at least as long as 'value', but could be longer if we - // need to unpack unicode characters. - var bytes = []; - for (var i = 0; i < value.length; i++) { - var c = value.charCodeAt(i); - if (c < 128) { - bytes.push(c); - } else if (c < 2048) { - bytes.push((c >> 6) | 192); - bytes.push((c & 63) | 128); - } else { - bytes.push((c >> 12) | 224); - bytes.push(((c >> 6) & 63) | 128); - bytes.push((c & 63) | 128); - } - } - - this.rawWriteUnsignedVarint32(bytes.length); - this.appendArray_(bytes); + var bookmark = this.beginDelimited_(field); + this.encoder_.writeString(value); + this.endDelimited_(bookmark); }; /** * Writes an arbitrary byte field to the buffer. Note - to match the behavior * of the C++ implementation, empty byte arrays _are_ serialized. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. - * @param {jspb.ByteSource} value The array of bytes to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. - * @param {boolean=} opt_stringIsRawBytes If `value` is a string, interpret it - * as a series of raw bytes (codepoints 0--255 inclusive) rather than base64 - * data. + * @param {?jspb.ByteSource} value The array of bytes to write. */ -jspb.BinaryWriter.prototype.writeBytes = - function(field, value, opt_buffer, opt_start, opt_end, - opt_stringIsRawBytes) { - if (value != null) { - this.rawWriteFieldHeader_(field, - jspb.BinaryConstants.WireType.DELIMITED); - this.rawWriteUnsignedVarint32(value.length); - this.rawWriteBytes( - jspb.utils.byteSourceToUint8Array(value, opt_stringIsRawBytes)); - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); - } -}; - - -/** - * Writes an arbitrary byte field to the buffer, with `opt_stringIsRawBytes` - * flag implicitly true. - * @param {number} field - * @param {jspb.ByteSource} value The array of bytes to write. - */ -jspb.BinaryWriter.prototype.writeBytesRawString = function(field, value) { - this.writeBytes(field, value, null, null, null, true); +jspb.BinaryWriter.prototype.writeBytes = function(field, value) { + if (value == null) return; + var bytes = jspb.utils.byteSourceToUint8Array(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); + this.encoder_.writeUnsignedVarint32(bytes.length); + this.appendUint8Array_(bytes); }; /** * Writes a message to the buffer. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @template MessageType * @param {number} field The field number. * @param {?MessageType} value The message to write. * @param {!jspb.WriterFunction} writerCallback Will be invoked with the value * to write and the writer to write it with. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ -jspb.BinaryWriter.prototype.writeMessage = - function(field, value, writerCallback, opt_buffer, opt_start, opt_end) { - if (value !== null) { - var bookmark = this.beginDelimited_(field); - - writerCallback(value, this); - - this.endDelimited_(bookmark); - } else if (opt_buffer && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); - } +jspb.BinaryWriter.prototype.writeMessage = function( + field, value, writerCallback) { + if (value == null) return; + var bookmark = this.beginDelimited_(field); + writerCallback(value, this); + this.endDelimited_(bookmark); }; @@ -1120,15 +742,12 @@ jspb.BinaryWriter.prototype.writeMessage = * @param {!jspb.WriterFunction} writerCallback Will be invoked with the value * to write and the writer to write it with. */ -jspb.BinaryWriter.prototype.writeGroup = - function(field, value, writerCallback) { - if (value) { - this.rawWriteFieldHeader_( - field, jspb.BinaryConstants.WireType.START_GROUP); - writerCallback(value, this); - this.rawWriteFieldHeader_( - field, jspb.BinaryConstants.WireType.END_GROUP); - } +jspb.BinaryWriter.prototype.writeGroup = function( + field, value, writerCallback) { + if (value == null) return; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.START_GROUP); + writerCallback(value, this); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.END_GROUP); }; @@ -1141,8 +760,8 @@ jspb.BinaryWriter.prototype.writeGroup = jspb.BinaryWriter.prototype.writeFixedHash64 = function(field, value) { if (value == null) return; goog.asserts.assert(value.length == 8); - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64); - this.rawWriteFixedHash64(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.FIXED64); + this.encoder_.writeFixedHash64(value); }; @@ -1155,8 +774,8 @@ jspb.BinaryWriter.prototype.writeFixedHash64 = function(field, value) { jspb.BinaryWriter.prototype.writeVarintHash64 = function(field, value) { if (value == null) return; goog.asserts.assert(value.length == 8); - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); - this.rawWriteVarintHash64(value); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.VARINT); + this.encoder_.writeVarintHash64(value); }; @@ -1196,10 +815,26 @@ jspb.BinaryWriter.prototype.writeRepeatedSignedVarint32_ = * @param {?Array.} value The array of ints to write. * @private */ -jspb.BinaryWriter.prototype.writeRepeatedVarint_ = function(field, value) { +jspb.BinaryWriter.prototype.writeRepeatedUnsignedVarint64_ = + function(field, value) { if (value == null) return; for (var i = 0; i < value.length; i++) { - this.writeVarint_(field, value[i]); + this.writeUnsignedVarint64_(field, value[i]); + } +}; + + +/** + * Writes an array of numbers to the buffer as a repeated varint field. + * @param {number} field The field number. + * @param {?Array.} value The array of ints to write. + * @private + */ +jspb.BinaryWriter.prototype.writeRepeatedSignedVarint64_ = + function(field, value) { + if (value == null) return; + for (var i = 0; i < value.length; i++) { + this.writeSignedVarint64_(field, value[i]); } }; @@ -1227,7 +862,7 @@ jspb.BinaryWriter.prototype.writeRepeatedZigzag32_ = function(field, value) { jspb.BinaryWriter.prototype.writeRepeatedZigzag_ = function(field, value) { if (value == null) return; for (var i = 0; i < value.length; i++) { - this.writeZigzagVarint_(field, value[i]); + this.writeZigzagVarint64_(field, value[i]); } }; @@ -1262,7 +897,7 @@ jspb.BinaryWriter.prototype.writeRepeatedInt32String = * @param {?Array.} value The array of ints to write. */ jspb.BinaryWriter.prototype.writeRepeatedInt64 = - jspb.BinaryWriter.prototype.writeRepeatedVarint_; + jspb.BinaryWriter.prototype.writeRepeatedSignedVarint64_; /** @@ -1312,7 +947,7 @@ jspb.BinaryWriter.prototype.writeRepeatedUint32String = * @param {?Array.} value The array of ints to write. */ jspb.BinaryWriter.prototype.writeRepeatedUint64 = - jspb.BinaryWriter.prototype.writeRepeatedVarint_; + jspb.BinaryWriter.prototype.writeRepeatedUnsignedVarint64_; /** @@ -1469,96 +1104,54 @@ jspb.BinaryWriter.prototype.writeRepeatedString = function(field, value) { /** * Writes an array of arbitrary byte fields to the buffer. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. - * @param {?Array.} value - * The arrays of arrays of bytes to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. - * @param {boolean=} opt_stringIsRawBytes Any values that are strings are - * interpreted as raw bytes rather than base64 data. + * @param {?Array.} value The arrays of arrays of bytes to + * write. */ -jspb.BinaryWriter.prototype.writeRepeatedBytes = - function(field, value, opt_buffer, opt_start, opt_end, - opt_stringIsRawBytes) { - if (value != null) { - for (var i = 0; i < value.length; i++) { - this.writeBytes(field, value[i], null, null, null, opt_stringIsRawBytes); - } - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writeRepeatedBytes = function(field, value) { + if (value == null) return; + for (var i = 0; i < value.length; i++) { + this.writeBytes(field, value[i]); } }; -/** - * Writes an array of arbitrary byte fields to the buffer, with - * `opt_stringIsRawBytes` implicitly true. - * @param {number} field - * @param {?Array.} value - */ -jspb.BinaryWriter.prototype.writeRepeatedBytesRawString = - function(field, value) { - this.writeRepeatedBytes(field, value, null, null, null, true); -}; - - /** * Writes an array of messages to the buffer. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @template MessageType * @param {number} field The field number. - * @param {?Array.} value The array of messages to + * @param {?Array.} value The array of messages to * write. * @param {!jspb.WriterFunction} writerCallback Will be invoked with the value * to write and the writer to write it with. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ -jspb.BinaryWriter.prototype.writeRepeatedMessage = - function(field, value, writerCallback, opt_buffer, opt_start, opt_end) { - if (value) { - for (var i = 0; i < value.length; i++) { - var bookmark = this.beginDelimited_(field); - - writerCallback(value[i], this); - - this.endDelimited_(bookmark); - } - } else if (opt_buffer && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writeRepeatedMessage = function( + field, value, writerCallback) { + if (value == null) return; + for (var i = 0; i < value.length; i++) { + var bookmark = this.beginDelimited_(field); + writerCallback(value[i], this); + this.endDelimited_(bookmark); } }; /** * Writes an array of group messages to the buffer. - * * @template MessageType * @param {number} field The field number. - * @param {?Array.} value The array of messages to + * @param {?Array.} value The array of messages to * write. * @param {!jspb.WriterFunction} writerCallback Will be invoked with the value * to write and the writer to write it with. */ -jspb.BinaryWriter.prototype.writeRepeatedGroup = - function(field, value, writerCallback) { - if (value) { - for (var i = 0; i < value.length; i++) { - this.rawWriteFieldHeader_( - field, jspb.BinaryConstants.WireType.START_GROUP); - writerCallback(value[i], this); - this.rawWriteFieldHeader_( - field, jspb.BinaryConstants.WireType.END_GROUP); - } +jspb.BinaryWriter.prototype.writeRepeatedGroup = function( + field, value, writerCallback) { + if (value == null) return; + for (var i = 0; i < value.length; i++) { + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.START_GROUP); + writerCallback(value[i], this); + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.END_GROUP); } }; @@ -1595,123 +1188,108 @@ jspb.BinaryWriter.prototype.writeRepeatedVarintHash64 = /** * Writes an array of numbers to the buffer as a packed varint field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. * @private */ -jspb.BinaryWriter.prototype.writePackedUnsignedVarint32_ = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null && value.length) { - var bookmark = this.beginDelimited_(field); - for (var i = 0; i < value.length; i++) { - this.rawWriteUnsignedVarint32(value[i]); - } - this.endDelimited_(bookmark); - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writePackedUnsignedVarint32_ = function( + field, value) { + if (value == null || !value.length) return; + var bookmark = this.beginDelimited_(field); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeUnsignedVarint32(value[i]); } + this.endDelimited_(bookmark); }; /** * Writes an array of numbers to the buffer as a packed varint field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. * @private */ -jspb.BinaryWriter.prototype.writePackedSignedVarint32_ = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null && value.length) { - var bookmark = this.beginDelimited_(field); - for (var i = 0; i < value.length; i++) { - this.rawWriteSignedVarint32(value[i]); - } - this.endDelimited_(bookmark); - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writePackedSignedVarint32_ = function( + field, value) { + if (value == null || !value.length) return; + var bookmark = this.beginDelimited_(field); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeSignedVarint32(value[i]); } + this.endDelimited_(bookmark); }; /** * Writes an array of numbers to the buffer as a packed varint field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. * @private */ -jspb.BinaryWriter.prototype.writePackedVarint_ = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null && value.length) { - var bookmark = this.beginDelimited_(field); - for (var i = 0; i < value.length; i++) { - this.rawWriteVarint(value[i]); - } - this.endDelimited_(bookmark); - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writePackedUnsignedVarint64_ = function( + field, value) { + if (value == null || !value.length) return; + var bookmark = this.beginDelimited_(field); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeUnsignedVarint64(value[i]); } + this.endDelimited_(bookmark); +}; + + +/** + * Writes an array of numbers to the buffer as a packed varint field. + * @param {number} field The field number. + * @param {?Array.} value The array of ints to write. + * @private + */ +jspb.BinaryWriter.prototype.writePackedSignedVarint64_ = function( + field, value) { + if (value == null || !value.length) return; + var bookmark = this.beginDelimited_(field); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeSignedVarint64(value[i]); + } + this.endDelimited_(bookmark); }; /** * Writes an array of numbers to the buffer as a packed zigzag field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. * @private */ -jspb.BinaryWriter.prototype.writePackedZigzag_ = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null && value.length) { - var bookmark = this.beginDelimited_(field); - for (var i = 0; i < value.length; i++) { - this.rawWriteZigzagVarint(value[i]); - } - this.endDelimited_(bookmark); - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writePackedZigzag32_ = function(field, value) { + if (value == null || !value.length) return; + var bookmark = this.beginDelimited_(field); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeZigzagVarint32(value[i]); } + this.endDelimited_(bookmark); +}; + + +/** + * Writes an array of numbers to the buffer as a packed zigzag field. + * @param {number} field The field number. + * @param {?Array.} value The array of ints to write. + * @private + */ +jspb.BinaryWriter.prototype.writePackedZigzag64_ = function(field, value) { + if (value == null || !value.length) return; + var bookmark = this.beginDelimited_(field); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeZigzagVarint64(value[i]); + } + this.endDelimited_(bookmark); }; /** * Writes an array of numbers to the buffer as a packed 32-bit int field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ jspb.BinaryWriter.prototype.writePackedInt32 = jspb.BinaryWriter.prototype.writePackedSignedVarint32_; @@ -1727,7 +1305,7 @@ jspb.BinaryWriter.prototype.writePackedInt32String = function(field, value) { if (value == null || !value.length) return; var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { - this.rawWriteSignedVarint32(parseInt(value[i], 10)); + this.encoder_.writeSignedVarint32(parseInt(value[i], 10)); } this.endDelimited_(bookmark); }; @@ -1737,12 +1315,9 @@ jspb.BinaryWriter.prototype.writePackedInt32String = function(field, value) { * Writes an array of numbers to the buffer as a packed 64-bit int field. * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ jspb.BinaryWriter.prototype.writePackedInt64 = - jspb.BinaryWriter.prototype.writePackedVarint_; + jspb.BinaryWriter.prototype.writePackedSignedVarint64_; /** @@ -1757,7 +1332,7 @@ jspb.BinaryWriter.prototype.writePackedInt64String = var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { var num = jspb.arith.Int64.fromString(value[i]); - this.rawWriteVarintFromNum(num); + this.encoder_.writeSplitVarint64(num.lo, num.hi); } this.endDelimited_(bookmark); }; @@ -1765,15 +1340,8 @@ jspb.BinaryWriter.prototype.writePackedInt64String = /** * Writes an array numbers to the buffer as a packed unsigned 32-bit int field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ jspb.BinaryWriter.prototype.writePackedUint32 = jspb.BinaryWriter.prototype.writePackedUnsignedVarint32_; @@ -1790,7 +1358,7 @@ jspb.BinaryWriter.prototype.writePackedUint32String = if (value == null || !value.length) return; var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { - this.rawWriteUnsignedVarint32(parseInt(value[i], 10)); + this.encoder_.writeUnsignedVarint32(parseInt(value[i], 10)); } this.endDelimited_(bookmark); }; @@ -1798,18 +1366,11 @@ jspb.BinaryWriter.prototype.writePackedUint32String = /** * Writes an array numbers to the buffer as a packed unsigned 64-bit int field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ jspb.BinaryWriter.prototype.writePackedUint64 = - jspb.BinaryWriter.prototype.writePackedVarint_; + jspb.BinaryWriter.prototype.writePackedUnsignedVarint64_; /** @@ -1824,7 +1385,7 @@ jspb.BinaryWriter.prototype.writePackedUint64String = var bookmark = this.beginDelimited_(field); for (var i = 0; i < value.length; i++) { var num = jspb.arith.UInt64.fromString(value[i]); - this.rawWriteVarintFromNum(num); + this.encoder_.writeSplitVarint64(num.lo, num.hi); } this.endDelimited_(bookmark); }; @@ -1832,240 +1393,154 @@ jspb.BinaryWriter.prototype.writePackedUint64String = /** * Writes an array numbers to the buffer as a packed signed 32-bit int field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ jspb.BinaryWriter.prototype.writePackedSint32 = - jspb.BinaryWriter.prototype.writePackedZigzag_; + jspb.BinaryWriter.prototype.writePackedZigzag32_; /** * Writes an array numbers to the buffer as a packed signed 64-bit int field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ jspb.BinaryWriter.prototype.writePackedSint64 = - jspb.BinaryWriter.prototype.writePackedZigzag_; + jspb.BinaryWriter.prototype.writePackedZigzag64_; /** * Writes an array of numbers to the buffer as a packed fixed32 field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ -jspb.BinaryWriter.prototype.writePackedFixed32 = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null && value.length) { - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); - this.rawWriteUnsignedVarint32(value.length * 4); - for (var i = 0; i < value.length; i++) { - this.rawWriteUint32(value[i]); - } - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writePackedFixed32 = function(field, value) { + if (value == null || !value.length) return; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); + this.encoder_.writeUnsignedVarint32(value.length * 4); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeUint32(value[i]); } }; /** * Writes an array of numbers to the buffer as a packed fixed64 field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ -jspb.BinaryWriter.prototype.writePackedFixed64 = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null && value.length) { - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); - this.rawWriteUnsignedVarint32(value.length * 8); - for (var i = 0; i < value.length; i++) { - this.rawWriteUint64(value[i]); - } - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writePackedFixed64 = function(field, value) { + if (value == null || !value.length) return; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); + this.encoder_.writeUnsignedVarint32(value.length * 8); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeUint64(value[i]); } }; /** * Writes an array of numbers to the buffer as a packed sfixed32 field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ -jspb.BinaryWriter.prototype.writePackedSfixed32 = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null && value.length) { - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); - this.rawWriteUnsignedVarint32(value.length * 4); - for (var i = 0; i < value.length; i++) { - this.rawWriteInt32(value[i]); - } - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writePackedSfixed32 = function(field, value) { + if (value == null || !value.length) return; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); + this.encoder_.writeUnsignedVarint32(value.length * 4); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeInt32(value[i]); } }; /** * Writes an array of numbers to the buffer as a packed sfixed64 field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ -jspb.BinaryWriter.prototype.writePackedSfixed64 = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null) { - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); - this.rawWriteUnsignedVarint32(value.length * 8); - for (var i = 0; i < value.length; i++) { - this.rawWriteInt64(value[i]); - } - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writePackedSfixed64 = function(field, value) { + if (value == null || !value.length) return; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); + this.encoder_.writeUnsignedVarint32(value.length * 8); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeInt64(value[i]); } }; /** * Writes an array of numbers to the buffer as a packed float field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ -jspb.BinaryWriter.prototype.writePackedFloat = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null && value.length) { - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); - this.rawWriteUnsignedVarint32(value.length * 4); - for (var i = 0; i < value.length; i++) { - this.rawWriteFloat(value[i]); - } - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writePackedFloat = function(field, value) { + if (value == null || !value.length) return; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); + this.encoder_.writeUnsignedVarint32(value.length * 4); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeFloat(value[i]); } }; /** * Writes an array of numbers to the buffer as a packed double field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ -jspb.BinaryWriter.prototype.writePackedDouble = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null && value.length) { - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); - this.rawWriteUnsignedVarint32(value.length * 8); - for (var i = 0; i < value.length; i++) { - this.rawWriteDouble(value[i]); - } - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writePackedDouble = function(field, value) { + if (value == null || !value.length) return; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); + this.encoder_.writeUnsignedVarint32(value.length * 8); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeDouble(value[i]); } }; /** * Writes an array of booleans to the buffer as a packed bool field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ -jspb.BinaryWriter.prototype.writePackedBool = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null && value.length) { - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); - this.rawWriteUnsignedVarint32(value.length); - for (var i = 0; i < value.length; i++) { - this.rawWriteBool(value[i]); - } - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writePackedBool = function(field, value) { + if (value == null || !value.length) return; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); + this.encoder_.writeUnsignedVarint32(value.length); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeBool(value[i]); } }; /** * Writes an array of enums to the buffer as a packed enum field. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of ints to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ -jspb.BinaryWriter.prototype.writePackedEnum = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null && value.length) { - var bookmark = this.beginDelimited_(field); - for (var i = 0; i < value.length; i++) { - this.rawWriteEnum(value[i]); - } - this.endDelimited_(bookmark); - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writePackedEnum = function(field, value) { + if (value == null || !value.length) return; + var bookmark = this.beginDelimited_(field); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeEnum(value[i]); + } + this.endDelimited_(bookmark); +}; + + +/** + * Writes a 64-bit hash string field (8 characters @ 8 bits of data each) to + * the buffer. + * @param {number} field The field number. + * @param {?Array.} value The array of hashes to write. + */ +jspb.BinaryWriter.prototype.writePackedFixedHash64 = function(field, value) { + if (value == null || !value.length) return; + this.writeFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); + this.encoder_.writeUnsignedVarint32(value.length * 8); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeFixedHash64(value[i]); } }; @@ -2073,52 +1548,14 @@ jspb.BinaryWriter.prototype.writePackedEnum = /** * Writes a 64-bit hash string field (8 characters @ 8 bits of data each) to * the buffer. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * * @param {number} field The field number. * @param {?Array.} value The array of hashes to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. */ -jspb.BinaryWriter.prototype.writePackedFixedHash64 = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null && value.length) { - this.rawWriteFieldHeader_(field, jspb.BinaryConstants.WireType.DELIMITED); - this.rawWriteUnsignedVarint32(value.length * 8); - for (var i = 0; i < value.length; i++) { - this.rawWriteFixedHash64(value[i]); - } - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); - } -}; - - -/** - * Writes a 64-bit hash string field (8 characters @ 8 bits of data each) to - * the buffer. - * - * If 'value' is null, this method will try and copy the pre-serialized value - * in 'opt_buffer' if present. - * - * @param {number} field The field number. - * @param {?Array.} value The array of hashes to write. - * @param {?Uint8Array=} opt_buffer A buffer containing pre-packed values. - * @param {?number=} opt_start The starting point in the above buffer. - * @param {?number=} opt_end The ending point in the above buffer. - */ -jspb.BinaryWriter.prototype.writePackedVarintHash64 = - function(field, value, opt_buffer, opt_start, opt_end) { - if (value != null && value.length) { - var bookmark = this.beginDelimited_(field); - for (var i = 0; i < value.length; i++) { - this.rawWriteVarintHash64(value[i]); - } - this.endDelimited_(bookmark); - } else if ((opt_buffer != null) && (opt_start != null) && (opt_end != null)) { - this.rawWriteByteRange(opt_buffer, opt_start, opt_end); +jspb.BinaryWriter.prototype.writePackedVarintHash64 = function(field, value) { + if (value == null || !value.length) return; + var bookmark = this.beginDelimited_(field); + for (var i = 0; i < value.length; i++) { + this.encoder_.writeVarintHash64(value[i]); } + this.endDelimited_(bookmark); }; diff --git a/js/commonjs/export_testdeps.js b/js/commonjs/export_testdeps.js new file mode 100644 index 000000000..59c77ca2c --- /dev/null +++ b/js/commonjs/export_testdeps.js @@ -0,0 +1,18 @@ +/** + * @fileoverview Export symbols needed by tests in CommonJS style. + * + * This file is like export.js, but for symbols that are only used by tests. + * However we exclude assert functions here, because they are exported into + * the global namespace, so those are handled as a special case in + * export_asserts.js. + */ + +goog.require('goog.crypt.base64'); +goog.require('jspb.arith.Int64'); +goog.require('jspb.arith.UInt64'); +goog.require('jspb.BinaryEncoder'); +goog.require('jspb.BinaryDecoder'); +goog.require('jspb.utils'); + +exports.goog = goog; +exports.jspb = jspb; diff --git a/js/commonjs/rewrite_tests_for_commonjs.js b/js/commonjs/rewrite_tests_for_commonjs.js index ffa877220..b6d90d287 100644 --- a/js/commonjs/rewrite_tests_for_commonjs.js +++ b/js/commonjs/rewrite_tests_for_commonjs.js @@ -61,6 +61,18 @@ function camelCase(str) { var module = null; var pkg = null; + +// Header: goes in every file at the top. +console.log("var global = Function('return this')();"); +console.log("var googleProtobuf = require('google-protobuf');"); +console.log("var testdeps = require('testdeps_commonjs');"); +console.log("global.goog = testdeps.goog;"); +console.log("global.jspb = testdeps.jspb;"); +console.log("var asserts = require('closure_asserts_commonjs');"); +console.log(""); +console.log("// Bring asserts into the global namespace."); +console.log("googleProtobuf.object.extend(global, asserts);"); + lineReader.on('line', function(line) { var isRequire = line.match(/goog\.require\('([^']*)'\)/); var isLoadFromFile = line.match(/CommonJS-LoadFromFile: (\S*) (.*)/); @@ -72,14 +84,6 @@ lineReader.on('line', function(line) { console.log("googleProtobuf.exportSymbol('" + fullSym + "', " + module + sym + ', global);'); } } else if (isLoadFromFile) { - if (!module) { - console.log("var googleProtobuf = require('google-protobuf');"); - console.log("var asserts = require('closure_asserts_commonjs');"); - console.log("var global = Function('return this')();"); - console.log(""); - console.log("// Bring asserts into the global namespace."); - console.log("googleProtobuf.object.extend(global, asserts);"); - } var module_path = isLoadFromFile[1].split('/'); module = camelCase(module_path[module_path.length - 1]); pkg = isLoadFromFile[2]; diff --git a/js/debug.js b/js/debug.js index 483891189..3701a0957 100644 --- a/js/debug.js +++ b/js/debug.js @@ -68,7 +68,7 @@ jspb.debug.dump = function(message) { * Recursively introspects a message and the values its getters return to * make a best effort in creating a human readable representation of the * message. - * @param {*} thing A jspb.Message, Array or primitive type to dump. + * @param {?} thing A jspb.Message, Array or primitive type to dump. * @return {*} * @private */ diff --git a/js/debug_test.js b/js/debug_test.js index d7bf3768f..01cbf891a 100644 --- a/js/debug_test.js +++ b/js/debug_test.js @@ -41,6 +41,7 @@ goog.require('proto.jspb.test.IsExtension'); goog.require('proto.jspb.test.Simple1'); + describe('debugTest', function() { it('testSimple1', function() { if (COMPILED) { diff --git a/js/gulpfile.js b/js/gulpfile.js index 36fd9fda0..c5220153b 100644 --- a/js/gulpfile.js +++ b/js/gulpfile.js @@ -1,7 +1,11 @@ var gulp = require('gulp'); -var exec = require('child_process').exec; +var execFile = require('child_process').execFile; var glob = require('glob'); +function exec(command, cb) { + execFile('sh', ['-c', command], cb); +} + var protoc = process.env.PROTOC || '../src/protoc'; gulp.task('genproto_closure', function (cb) { @@ -42,7 +46,16 @@ gulp.task('commonjs_asserts', function (cb) { }); }); -gulp.task('make_commonjs_out', ['dist', 'genproto_commonjs', 'commonjs_asserts'], function (cb) { +gulp.task('commonjs_testdeps', function (cb) { + exec('mkdir -p commonjs_out/test_node_modules && ./node_modules/google-closure-library/closure/bin/calcdeps.py -i commonjs/export_testdeps.js -p . -p node_modules/google-closure-library/closure -o compiled --compiler_jar node_modules/google-closure-compiler/compiler.jar > commonjs_out/test_node_modules/testdeps_commonjs.js', + function (err, stdout, stderr) { + console.log(stdout); + console.log(stderr); + cb(err); + }); +}); + +gulp.task('make_commonjs_out', ['dist', 'genproto_commonjs', 'commonjs_asserts', 'commonjs_testdeps'], function (cb) { // TODO(haberman): minify this more aggressively. // Will require proper externs/exports. var cmd = "mkdir -p commonjs_out/binary && mkdir -p commonjs_out/test_node_modules && "; diff --git a/js/message.js b/js/message.js index cef9aefd3..813e6b8cd 100644 --- a/js/message.js +++ b/js/message.js @@ -39,8 +39,8 @@ goog.provide('jspb.Message'); goog.require('goog.array'); goog.require('goog.asserts'); +goog.require('goog.crypt.base64'); goog.require('goog.json'); -goog.require('goog.object'); // Not needed in compilation units that have no protos with xids. goog.forwardDeclare('xid.String'); @@ -119,6 +119,14 @@ jspb.ExtensionFieldInfo = function(fieldNumber, fieldName, ctor, toObjectFn, }; +/** + * @return {boolean} Does this field represent a sub Message? + */ +jspb.ExtensionFieldInfo.prototype.isMessageType = function() { + return !!this.ctor; +}; + + /** * Base class for all JsPb messages. * @constructor @@ -165,6 +173,14 @@ goog.define('jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS', COMPILED); // TODO(b/19419436) Turn this on by default. +/** + * Does this browser support Uint8Aray typed arrays? + * @type {boolean} + * @private + */ +jspb.Message.SUPPORTS_UINT8ARRAY_ = (typeof Uint8Array == 'function'); + + /** * The internal data array. * @type {!Array} @@ -210,6 +226,14 @@ jspb.Message.prototype.pivot_; jspb.Message.prototype.messageId_; +/** + * Repeated float or double fields which have been converted to include only + * numbers and not strings holding "NaN", "Infinity" and "-Infinity". + * @private {!Object|undefined} + */ +jspb.Message.prototype.convertedFloatingPointFields_; + + /** * The xid of this proto type (The same for all instances of a proto). Provides * a way to identify a proto by stable obfuscated name. @@ -284,6 +308,8 @@ jspb.Message.initialize = function( msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0; msg.array = data; jspb.Message.materializeExtensionObject_(msg, suggestedPivot); + msg.convertedFloatingPointFields_ = {}; + if (repeatedFields) { for (var i = 0; i < repeatedFields.length; i++) { var fieldNumber = repeatedFields[i]; @@ -419,8 +445,9 @@ jspb.Message.toObjectList = function(field, toObjectFn, opt_includeInstance) { * @param {!jspb.Message} proto The proto whose extensions to convert. * @param {!Object} obj The Soy object to add converted extension data to. * @param {!Object} extensions The proto class' registered extensions. - * @param {function(jspb.ExtensionFieldInfo) : *} getExtensionFn The proto - * class' getExtension function. Passed for effective dead code removal. + * @param {function(this:?, jspb.ExtensionFieldInfo) : *} getExtensionFn + * The proto class' getExtension function. Passed for effective dead code + * removal. * @param {boolean=} opt_includeInstance Whether to include the JSPB instance * for transitional soy proto support: http://goto/soy-param-migration */ @@ -472,7 +499,7 @@ jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions, } var value = getExtensionFn.call(proto, fieldInfo); if (value) { - if (fieldInfo.ctor) { // is this a message type? + if (fieldInfo.isMessageType()) { // If the message type of the extension was generated without binary // support, there may not be a binary message serializer function, and // we can't know when we codegen the extending message that the extended @@ -517,8 +544,7 @@ jspb.Message.readBinaryExtension = function(msg, reader, extensions, } var value; - if (fieldInfo.ctor) { - // Message type. + if (fieldInfo.isMessageType()) { value = new fieldInfo.ctor(); fieldInfo.binaryReaderFn.call( reader, value, fieldInfo.binaryMessageDeserializeFn); @@ -566,6 +592,130 @@ jspb.Message.getField = function(msg, fieldNumber) { }; +/** + * Gets the value of an optional float or double field. + * @param {!jspb.Message} msg A jspb proto. + * @param {number} fieldNumber The field number. + * @return {?number|undefined} The field's value. + * @protected + */ +jspb.Message.getOptionalFloatingPointField = function(msg, fieldNumber) { + var value = jspb.Message.getField(msg, fieldNumber); + // Converts "NaN", "Infinity" and "-Infinity" to their corresponding numbers. + return value == null ? value : +value; +}; + + +/** + * Gets the value of a repeated float or double field. + * @param {!jspb.Message} msg A jspb proto. + * @param {number} fieldNumber The field number. + * @return {!Array} The field's value. + * @protected + */ +jspb.Message.getRepeatedFloatingPointField = function(msg, fieldNumber) { + var values = jspb.Message.getField(msg, fieldNumber); + if (!msg.convertedFloatingPointFields_) { + msg.convertedFloatingPointFields_ = {}; + } + if (!msg.convertedFloatingPointFields_[fieldNumber]) { + for (var i = 0; i < values.length; i++) { + // Converts "NaN", "Infinity" and "-Infinity" to their corresponding + // numbers. + values[i] = +values[i]; + } + msg.convertedFloatingPointFields_[fieldNumber] = true; + } + return /** @type {!Array} */ (values); +}; + + +/** + * Coerce a 'bytes' field to a base 64 string. + * @param {string|Uint8Array|null} value + * @return {?string} The field's coerced value. + */ +jspb.Message.bytesAsB64 = function(value) { + if (value == null || goog.isString(value)) { + return value; + } + if (jspb.Message.SUPPORTS_UINT8ARRAY_ && value instanceof Uint8Array) { + return goog.crypt.base64.encodeByteArray(value); + } + goog.asserts.fail('Cannot coerce to b64 string: ' + goog.typeOf(value)); + return null; +}; + + +/** + * Coerce a 'bytes' field to a Uint8Array byte buffer. + * Note that Uint8Array is not supported on IE versions before 10 nor on Opera + * Mini. @see http://caniuse.com/Uint8Array + * @param {string|Uint8Array|null} value + * @return {?Uint8Array} The field's coerced value. + */ +jspb.Message.bytesAsU8 = function(value) { + if (value == null || value instanceof Uint8Array) { + return value; + } + if (goog.isString(value)) { + return goog.crypt.base64.decodeStringToUint8Array(value); + } + goog.asserts.fail('Cannot coerce to Uint8Array: ' + goog.typeOf(value)); + return null; +}; + + +/** + * Coerce a repeated 'bytes' field to an array of base 64 strings. + * Note: the returned array should be treated as immutable. + * @param {!Array|!Array} value + * @return {!Array} The field's coerced value. + */ +jspb.Message.bytesListAsB64 = function(value) { + jspb.Message.assertConsistentTypes_(value); + if (!value.length || goog.isString(value[0])) { + return /** @type {!Array} */ (value); + } + return goog.array.map(value, jspb.Message.bytesAsB64); +}; + + +/** + * Coerce a repeated 'bytes' field to an array of Uint8Array byte buffers. + * Note: the returned array should be treated as immutable. + * Note that Uint8Array is not supported on IE versions before 10 nor on Opera + * Mini. @see http://caniuse.com/Uint8Array + * @param {!Array|!Array} value + * @return {!Array} The field's coerced value. + */ +jspb.Message.bytesListAsU8 = function(value) { + jspb.Message.assertConsistentTypes_(value); + if (!value.length || value[0] instanceof Uint8Array) { + return /** @type {!Array} */ (value); + } + return goog.array.map(value, jspb.Message.bytesAsU8); +}; + + +/** + * Asserts that all elements of an array are of the same type. + * @param {Array?} array The array to test. + * @private + */ +jspb.Message.assertConsistentTypes_ = function(array) { + if (goog.DEBUG && array && array.length > 1) { + var expected = goog.typeOf(array[0]); + goog.array.forEach(array, function(e) { + if (goog.typeOf(e) != expected) { + goog.asserts.fail('Inconsistent type in JSPB repeated field array. ' + + 'Got ' + goog.typeOf(e) + ' expected ' + expected); + } + }); + } +}; + + /** * Gets the value of a non-extension primitive field, with proto3 (non-nullable * primitives) semantics. Returns `defaultValue` if the field is not otherwise @@ -841,7 +991,7 @@ jspb.Message.prototype.getExtension = function(fieldInfo) { } var fieldNumber = fieldInfo.fieldIndex; if (fieldInfo.isRepeated) { - if (fieldInfo.ctor) { + if (fieldInfo.isMessageType()) { if (!this.wrappers_[fieldNumber]) { this.wrappers_[fieldNumber] = goog.array.map(this.extensionObject_[fieldNumber] || [], @@ -854,7 +1004,7 @@ jspb.Message.prototype.getExtension = function(fieldInfo) { return this.extensionObject_[fieldNumber]; } } else { - if (fieldInfo.ctor) { + if (fieldInfo.isMessageType()) { if (!this.wrappers_[fieldNumber] && this.extensionObject_[fieldNumber]) { this.wrappers_[fieldNumber] = new fieldInfo.ctor( /** @type {Array|undefined} */ ( @@ -871,7 +1021,8 @@ jspb.Message.prototype.getExtension = function(fieldInfo) { /** * Sets the value of the extension field in the extended object. * @param {jspb.ExtensionFieldInfo} fieldInfo Specifies the field to set. - * @param {jspb.Message|string|number|boolean|Array} value The value to set. + * @param {jspb.Message|string|Uint8Array|number|boolean|Array?} value The value + * to set. */ jspb.Message.prototype.setExtension = function(fieldInfo, value) { if (!this.wrappers_) { @@ -881,7 +1032,7 @@ jspb.Message.prototype.setExtension = function(fieldInfo, value) { var fieldNumber = fieldInfo.fieldIndex; if (fieldInfo.isRepeated) { value = value || []; - if (fieldInfo.ctor) { + if (fieldInfo.isMessageType()) { this.wrappers_[fieldNumber] = value; this.extensionObject_[fieldNumber] = goog.array.map( /** @type {Array} */ (value), function(msg) { @@ -891,7 +1042,7 @@ jspb.Message.prototype.setExtension = function(fieldInfo, value) { this.extensionObject_[fieldNumber] = value; } } else { - if (fieldInfo.ctor) { + if (fieldInfo.isMessageType()) { this.wrappers_[fieldNumber] = value; this.extensionObject_[fieldNumber] = value ? value.toArray() : value; } else { @@ -957,6 +1108,33 @@ jspb.Message.equals = function(m1, m2) { }; +/** + * Compares two message extension fields recursively. + * @param {!Object} extension1 The first field. + * @param {!Object} extension2 The second field. + * @return {boolean} true if the extensions are null/undefined, or otherwise + * equal. + */ +jspb.Message.compareExtensions = function(extension1, extension2) { + extension1 = extension1 || {}; + extension2 = extension2 || {}; + + var keys = {}; + for (var name in extension1) { + keys[name] = 0; + } + for (var name in extension2) { + keys[name] = 0; + } + for (name in keys) { + if (!jspb.Message.compareFields(extension1[name], extension2[name])) { + return false; + } + } + return true; +}; + + /** * Compares two message fields recursively. * @param {*} field1 The first field. @@ -964,43 +1142,77 @@ jspb.Message.equals = function(m1, m2) { * @return {boolean} true if the fields are null/undefined, or otherwise equal. */ jspb.Message.compareFields = function(field1, field2) { - if (goog.isObject(field1) && goog.isObject(field2)) { - var keys = {}, name, extensionObject1, extensionObject2; - for (name in field1) { - field1.hasOwnProperty(name) && (keys[name] = 0); + // If the fields are trivially equal, they're equal. + if (field1 == field2) return true; + + // If the fields aren't trivially equal and one of them isn't an object, + // they can't possibly be equal. + if (!goog.isObject(field1) || !goog.isObject(field2)) { + return false; + } + + // We have two objects. If they're different types, they're not equal. + field1 = /** @type {!Object} */(field1); + field2 = /** @type {!Object} */(field2); + if (field1.constructor != field2.constructor) return false; + + // If both are Uint8Arrays, compare them element-by-element. + if (jspb.Message.SUPPORTS_UINT8ARRAY_ && field1.constructor === Uint8Array) { + var bytes1 = /** @type {!Uint8Array} */(field1); + var bytes2 = /** @type {!Uint8Array} */(field2); + if (bytes1.length != bytes2.length) return false; + for (var i = 0; i < bytes1.length; i++) { + if (bytes1[i] != bytes2[i]) return false; } - for (name in field2) { - field2.hasOwnProperty(name) && (keys[name] = 0); - } - for (name in keys) { - var val1 = field1[name], val2 = field2[name]; - if (goog.isObject(val1) && !goog.isArray(val1)) { - if (extensionObject1 !== undefined) { - throw new Error('invalid jspb state'); - } - extensionObject1 = goog.object.isEmpty(val1) ? undefined : val1; + return true; + } + + // If they're both Arrays, compare them element by element except for the + // optional extension objects at the end, which we compare separately. + if (field1.constructor === Array) { + var extension1 = undefined; + var extension2 = undefined; + + var length = Math.max(field1.length, field2.length); + for (var i = 0; i < length; i++) { + var val1 = field1[i]; + var val2 = field2[i]; + + if (val1 && (val1.constructor == Object)) { + goog.asserts.assert(extension1 === undefined); + goog.asserts.assert(i === field1.length - 1); + extension1 = val1; val1 = undefined; } - if (goog.isObject(val2) && !goog.isArray(val2)) { - if (extensionObject2 !== undefined) { - throw new Error('invalid jspb state'); - } - extensionObject2 = goog.object.isEmpty(val2) ? undefined : val2; + + if (val2 && (val2.constructor == Object)) { + goog.asserts.assert(extension2 === undefined); + goog.asserts.assert(i === field2.length - 1); + extension2 = val2; val2 = undefined; } + if (!jspb.Message.compareFields(val1, val2)) { return false; } } - if (extensionObject1 || extensionObject2) { - return jspb.Message.compareFields(extensionObject1, extensionObject2); + + if (extension1 || extension2) { + extension1 = extension1 || {}; + extension2 = extension2 || {}; + return jspb.Message.compareExtensions(extension1, extension2); } + return true; } - // Primitive fields, null and undefined compare as equal. - // This also forces booleans and 0/1 to compare as equal to ensure - // compatibility with the jspb serializer. - return field1 == field2; + + // If they're both plain Objects (i.e. extensions), compare them as + // extensions. + if (field1.constructor === Object) { + return jspb.Message.compareExtensions(field1, field2); + } + + throw new Error('Invalid type in JSPB array'); }; diff --git a/js/message_test.js b/js/message_test.js index f572188e6..01add5f1d 100644 --- a/js/message_test.js +++ b/js/message_test.js @@ -53,6 +53,8 @@ goog.require('proto.jspb.test.Complex'); goog.require('proto.jspb.test.DefaultValues'); goog.require('proto.jspb.test.Empty'); goog.require('proto.jspb.test.EnumContainer'); +goog.require('proto.jspb.test.floatingMsgField'); +goog.require('proto.jspb.test.FloatingPointFields'); goog.require('proto.jspb.test.floatingStrField'); goog.require('proto.jspb.test.HasExtensions'); goog.require('proto.jspb.test.IndirectExtension'); @@ -60,7 +62,6 @@ goog.require('proto.jspb.test.IsExtension'); goog.require('proto.jspb.test.OptionalFields'); goog.require('proto.jspb.test.OuterEnum'); goog.require('proto.jspb.test.OuterMessage.Complex'); -goog.require('proto.jspb.test.simple1'); goog.require('proto.jspb.test.Simple1'); goog.require('proto.jspb.test.Simple2'); goog.require('proto.jspb.test.SpecialCases'); @@ -74,7 +75,7 @@ goog.require('proto.jspb.test.TestReservedNamesExtension'); // CommonJS-LoadFromFile: test2_pb proto.jspb.test goog.require('proto.jspb.test.ExtensionMessage'); goog.require('proto.jspb.test.TestExtensionsMessage'); -goog.require('proto.jspb.test.floatingMsgField'); + @@ -98,12 +99,6 @@ describe('Message test suite', function() { assertEquals('some_bytes', data.getBytesField()); }); - it('testNestedMessage', function() { - var msg = new proto.jspb.test.OuterMessage.Complex(); - msg.setInnerComplexField(5); - assertObjectEquals({innerComplexField: 5}, msg.toObject()); - }); - it('testComplexConversion', function() { var data1 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1]; var data2 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1]; @@ -160,6 +155,13 @@ describe('Message test suite', function() { }); + it('testNestedComplexMessage', function() { + // Instantiate the message and set a unique field, just to ensure that we + // are not getting jspb.test.Complex instead. + var msg = new proto.jspb.test.OuterMessage.Complex(); + msg.setInnerComplexField(5); + }); + it('testSpecialCases', function() { // Note: Some property names are reserved in JavaScript. // These names are converted to the Js property named pb_. @@ -544,12 +546,6 @@ describe('Message test suite', function() { extendable.setExtension(proto.jspb.test.IndirectExtension.str, null); assertNull(extendable.getExtension(proto.jspb.test.IndirectExtension.str)); - // These assertions will only work properly in uncompiled mode. - // Extension fields defined on proto2 Descriptor messages are filtered out. - - // TODO(haberman): codegen changes to properly ignore descriptor.proto - // extensions need to be merged from google3. - // assertUndefined(proto.jspb.test.IsExtension['simpleOption']); // Extension fields with jspb.ignore = true are ignored. assertUndefined(proto.jspb.test.IndirectExtension['ignored']); @@ -907,7 +903,7 @@ describe('Message test suite', function() { message.getDefaultOneofACase()); message = - new proto.jspb.test.TestMessageWithOneof(new Array(9).concat(567,890)); + new proto.jspb.test.TestMessageWithOneof(new Array(9).concat(567, 890)); assertEquals(1234, message.getAone()); assertEquals(890, message.getAtwo()); assertEquals( @@ -936,7 +932,7 @@ describe('Message test suite', function() { message.getDefaultOneofBCase()); message = new proto.jspb.test.TestMessageWithOneof( - new Array(11).concat(567,890)); + new Array(11).concat(567, 890)); assertUndefined(message.getBone()); assertEquals(890, message.getBtwo()); assertEquals( @@ -989,4 +985,26 @@ describe('Message test suite', function() { assertEquals('y', array[4]); }); + it('testFloatingPointFieldsSupportNan', function() { + var assertNan = function(x) { + assertTrue('Expected ' + x + ' (' + goog.typeOf(x) + ') to be NaN.', + goog.isNumber(x) && isNaN(x)); + }; + + var message = new proto.jspb.test.FloatingPointFields([ + 'NaN', 'NaN', ['NaN', 'NaN'], 'NaN', + 'NaN', 'NaN', ['NaN', 'NaN'], 'NaN' + ]); + assertNan(message.getOptionalFloatField()); + assertNan(message.getRequiredFloatField()); + assertNan(message.getRepeatedFloatFieldList()[0]); + assertNan(message.getRepeatedFloatFieldList()[1]); + assertNan(message.getDefaultFloatField()); + assertNan(message.getOptionalDoubleField()); + assertNan(message.getRequiredDoubleField()); + assertNan(message.getRepeatedDoubleFieldList()[0]); + assertNan(message.getRepeatedDoubleFieldList()[1]); + assertNan(message.getDefaultDoubleField()); + }); + }); diff --git a/js/package.json b/js/package.json index 6418e5075..c16417cad 100644 --- a/js/package.json +++ b/js/package.json @@ -13,7 +13,7 @@ "glob": "~6.0.4" }, "scripts": { - "test": "./node_modules/gulp/bin/gulp.js test" + "test": "node ./node_modules/gulp/bin/gulp.js test" }, "repository": { "type": "git", diff --git a/js/proto3_test.js b/js/proto3_test.js index f8868716d..4dd7790f1 100644 --- a/js/proto3_test.js +++ b/js/proto3_test.js @@ -28,6 +28,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +goog.require('goog.crypt.base64'); goog.require('goog.testing.asserts'); // CommonJS-LoadFromFile: testbinary_pb proto.jspb.test @@ -37,31 +38,30 @@ goog.require('proto.jspb.test.ForeignMessage'); goog.require('proto.jspb.test.Proto3Enum'); goog.require('proto.jspb.test.TestProto3'); + +var BYTES = new Uint8Array([1, 2, 8, 9]); +var BYTES_B64 = goog.crypt.base64.encodeByteArray(BYTES); + + /** - * Helper: compare a bytes field to a string with codepoints 0--255. + * Helper: compare a bytes field to an expected value * @param {Uint8Array|string} arr - * @param {string} str + * @param {Uint8Array} expected * @return {boolean} */ -function bytesCompare(arr, str) { - if (arr.length != str.length) { +function bytesCompare(arr, expected) { + if (goog.isString(arr)) { + arr = goog.crypt.base64.decodeStringToUint8Array(arr); + } + if (arr.length != expected.length) { return false; } - if (typeof arr == 'string') { - for (var i = 0; i < arr.length; i++) { - if (arr.charCodeAt(i) != str.charCodeAt(i)) { - return false; - } + for (var i = 0; i < arr.length; i++) { + if (arr[i] != expected[i]) { + return false; } - return true; - } else { - for (var i = 0; i < arr.length; i++) { - if (arr[i] != str.charCodeAt(i)) { - return false; - } - } - return true; } + return true; } @@ -86,13 +86,17 @@ describe('proto3Test', function() { assertEquals(msg.getOptionalDouble(), 0); assertEquals(msg.getOptionalString(), ''); - // If/when we change bytes fields to return Uint8Array, we'll want to switch - // to this assertion instead: - //assertEquals(msg.getOptionalBytes() instanceof Uint8Array, true); + // TODO(b/26173701): when we change bytes fields default getter to return + // Uint8Array, we'll want to switch this assertion to match the u8 case. assertEquals(typeof msg.getOptionalBytes(), 'string'); - + assertEquals(msg.getOptionalBytes_asU8() instanceof Uint8Array, true); + assertEquals(typeof msg.getOptionalBytes_asB64(), 'string'); assertEquals(msg.getOptionalBytes().length, 0); - assertEquals(msg.getOptionalForeignEnum(), proto.jspb.test.Proto3Enum.PROTO3_FOO); + assertEquals(msg.getOptionalBytes_asU8().length, 0); + assertEquals(msg.getOptionalBytes_asB64(), ''); + + assertEquals(msg.getOptionalForeignEnum(), + proto.jspb.test.Proto3Enum.PROTO3_FOO); assertEquals(msg.getOptionalForeignMessage(), undefined); assertEquals(msg.getOptionalForeignMessage(), undefined); @@ -136,7 +140,7 @@ describe('proto3Test', function() { msg.setOptionalDouble(-1.5); msg.setOptionalBool(true); msg.setOptionalString('hello world'); - msg.setOptionalBytes('bytes'); + msg.setOptionalBytes(BYTES); var submsg = new proto.jspb.test.ForeignMessage(); submsg.setC(16); msg.setOptionalForeignMessage(submsg); @@ -156,7 +160,7 @@ describe('proto3Test', function() { msg.setRepeatedDoubleList([-1.5]); msg.setRepeatedBoolList([true]); msg.setRepeatedStringList(['hello world']); - msg.setRepeatedBytesList(['bytes']); + msg.setRepeatedBytesList([BYTES]); submsg = new proto.jspb.test.ForeignMessage(); submsg.setC(1000); msg.setRepeatedForeignMessageList([submsg]); @@ -181,7 +185,7 @@ describe('proto3Test', function() { assertEquals(msg.getOptionalDouble(), -1.5); assertEquals(msg.getOptionalBool(), true); assertEquals(msg.getOptionalString(), 'hello world'); - assertEquals(true, bytesCompare(msg.getOptionalBytes(), 'bytes')); + assertEquals(true, bytesCompare(msg.getOptionalBytes(), BYTES)); assertEquals(msg.getOptionalForeignMessage().getC(), 16); assertEquals(msg.getOptionalForeignEnum(), proto.jspb.test.Proto3Enum.PROTO3_BAR); @@ -201,7 +205,7 @@ describe('proto3Test', function() { assertElementsEquals(msg.getRepeatedBoolList(), [true]); assertElementsEquals(msg.getRepeatedStringList(), ['hello world']); assertEquals(msg.getRepeatedBytesList().length, 1); - assertEquals(true, bytesCompare(msg.getRepeatedBytesList()[0], 'bytes')); + assertEquals(true, bytesCompare(msg.getRepeatedBytesList()[0], BYTES)); assertEquals(msg.getRepeatedForeignMessageList().length, 1); assertEquals(msg.getRepeatedForeignMessageList()[0].getC(), 1000); assertElementsEquals(msg.getRepeatedForeignEnumList(), @@ -242,11 +246,12 @@ describe('proto3Test', function() { assertEquals(msg.getOneofString(), 'hello'); assertEquals(msg.getOneofBytes(), undefined); - msg.setOneofBytes('\u00FF\u00FF'); + msg.setOneofBytes(goog.crypt.base64.encodeString('\u00FF\u00FF')); assertEquals(msg.getOneofUint32(), undefined); assertEquals(msg.getOneofForeignMessage(), undefined); assertEquals(msg.getOneofString(), undefined); - assertEquals(msg.getOneofBytes(), '\u00FF\u00FF'); + assertEquals(msg.getOneofBytes_asB64(), + goog.crypt.base64.encodeString('\u00FF\u00FF')); }); @@ -267,7 +272,7 @@ describe('proto3Test', function() { msg.setOptionalBool(false); msg.setOptionalString('hello world'); msg.setOptionalString(''); - msg.setOptionalBytes('\u00FF\u00FF'); + msg.setOptionalBytes(goog.crypt.base64.encodeString('\u00FF\u00FF')); msg.setOptionalBytes(''); msg.setOptionalForeignMessage(new proto.jspb.test.ForeignMessage()); msg.setOptionalForeignMessage(null); @@ -280,4 +285,30 @@ describe('proto3Test', function() { var serialized = msg.serializeBinary(); assertEquals(0, serialized.length); }); + + /** + * Test that base64 string and Uint8Array are interchangeable in bytes fields. + */ + it('testBytesFieldsInterop', function() { + var msg = new proto.jspb.test.TestProto3(); + // Set as a base64 string and check all the getters work. + msg.setOptionalBytes(BYTES_B64); + assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + // Test binary serialize round trip doesn't break it. + msg = proto.jspb.test.TestProto3.deserializeBinary(msg.serializeBinary()); + assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + msg = new proto.jspb.test.TestProto3(); + // Set as a Uint8Array and check all the getters work. + msg.setOptionalBytes(BYTES); + assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES)); + assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES)); + + }); }); diff --git a/js/test.proto b/js/test.proto index 14418ac93..6b9dc8911 100644 --- a/js/test.proto +++ b/js/test.proto @@ -145,6 +145,17 @@ message DefaultValues { optional bytes bytes_field = 8 [default="moo"]; // Base64 encoding is "bW9v" } +message FloatingPointFields { + optional float optional_float_field = 1; + required float required_float_field = 2; + repeated float repeated_float_field = 3; + optional float default_float_field = 4 [default = 2.0]; + optional double optional_double_field = 5; + required double required_double_field = 6; + repeated double repeated_double_field = 7; + optional double default_double_field = 8 [default = 2.0]; +} + message TestClone { optional string str = 1; optional Simple1 simple1 = 3; @@ -216,3 +227,4 @@ message TestMessageWithOneof { int32 btwo = 13 [default = 1234]; } } + diff --git a/objectivec/DevTools/compile_testing_protos.sh b/objectivec/DevTools/compile_testing_protos.sh index e9c5fe611..82953130c 100755 --- a/objectivec/DevTools/compile_testing_protos.sh +++ b/objectivec/DevTools/compile_testing_protos.sh @@ -67,29 +67,36 @@ fi # Ensure the output dir exists mkdir -p "${OUTPUT_DIR}/google/protobuf" -CORE_PROTO_FILES=( \ - src/google/protobuf/unittest_arena.proto \ - src/google/protobuf/unittest_custom_options.proto \ - src/google/protobuf/unittest_enormous_descriptor.proto \ - src/google/protobuf/unittest_embed_optimize_for.proto \ - src/google/protobuf/unittest_empty.proto \ - src/google/protobuf/unittest_import.proto \ - src/google/protobuf/unittest_import_lite.proto \ - src/google/protobuf/unittest_lite.proto \ - src/google/protobuf/unittest_mset.proto \ - src/google/protobuf/unittest_mset_wire_format.proto \ - src/google/protobuf/unittest_no_arena.proto \ - src/google/protobuf/unittest_no_arena_import.proto \ - src/google/protobuf/unittest_no_generic_services.proto \ - src/google/protobuf/unittest_optimize_for.proto \ - src/google/protobuf/unittest.proto \ - src/google/protobuf/unittest_import_public.proto \ - src/google/protobuf/unittest_import_public_lite.proto \ - src/google/protobuf/unittest_drop_unknown_fields.proto \ - src/google/protobuf/unittest_preserve_unknown_enum.proto \ - src/google/protobuf/map_lite_unittest.proto \ - src/google/protobuf/map_proto2_unittest.proto \ - src/google/protobuf/map_unittest.proto \ +CORE_PROTO_FILES=( + src/google/protobuf/unittest_arena.proto + src/google/protobuf/unittest_custom_options.proto + src/google/protobuf/unittest_enormous_descriptor.proto + src/google/protobuf/unittest_embed_optimize_for.proto + src/google/protobuf/unittest_empty.proto + src/google/protobuf/unittest_import.proto + src/google/protobuf/unittest_import_lite.proto + src/google/protobuf/unittest_lite.proto + src/google/protobuf/unittest_mset.proto + src/google/protobuf/unittest_mset_wire_format.proto + src/google/protobuf/unittest_no_arena.proto + src/google/protobuf/unittest_no_arena_import.proto + src/google/protobuf/unittest_no_generic_services.proto + src/google/protobuf/unittest_optimize_for.proto + src/google/protobuf/unittest.proto + src/google/protobuf/unittest_import_public.proto + src/google/protobuf/unittest_import_public_lite.proto + src/google/protobuf/unittest_drop_unknown_fields.proto + src/google/protobuf/unittest_preserve_unknown_enum.proto + src/google/protobuf/map_lite_unittest.proto + src/google/protobuf/map_proto2_unittest.proto + src/google/protobuf/map_unittest.proto +) + +# The unittest_custom_options.proto extends the messages in descriptor.proto +# so we build it in to test extending in general. The library doesn't provide +# a descriptor as it doesn't use the classes/enums. +CORE_PROTO_FILES+=( + src/google/protobuf/descriptor.proto ) compile_proto() { @@ -104,12 +111,12 @@ for a_proto in "${CORE_PROTO_FILES[@]}" ; do compile_proto "${a_proto}" done -OBJC_PROTO_FILES=( \ - objectivec/Tests/unittest_cycle.proto \ - objectivec/Tests/unittest_runtime_proto2.proto \ - objectivec/Tests/unittest_runtime_proto3.proto \ - objectivec/Tests/unittest_objc.proto \ - objectivec/Tests/unittest_objc_startup.proto \ +OBJC_PROTO_FILES=( + objectivec/Tests/unittest_cycle.proto + objectivec/Tests/unittest_runtime_proto2.proto + objectivec/Tests/unittest_runtime_proto3.proto + objectivec/Tests/unittest_objc.proto + objectivec/Tests/unittest_objc_startup.proto ) for a_proto in "${OBJC_PROTO_FILES[@]}" ; do diff --git a/objectivec/DevTools/full_mac_build.sh b/objectivec/DevTools/full_mac_build.sh index c8681e26d..ff51d9f01 100755 --- a/objectivec/DevTools/full_mac_build.sh +++ b/objectivec/DevTools/full_mac_build.sh @@ -26,8 +26,9 @@ OPTIONS: Issue a clean before the normal build. -a, --autogen Start by rerunning autogen & configure. - -r, --regenerate-cpp-descriptors - The descriptor.proto is checked in generated, cause it to regenerate. + -r, --regenerate-descriptors + Run generate_descriptor_proto.sh to regenerate all the checked in + proto sources. -j #, --jobs # Force the number of parallel jobs (useful for debugging build issues). --core-only @@ -71,7 +72,7 @@ fi DO_AUTOGEN=no DO_CLEAN=no -REGEN_CPP_DESCRIPTORS=no +REGEN_DESCRIPTORS=no CORE_ONLY=no DO_XCODE_IOS_TESTS=yes DO_XCODE_OSX_TESTS=yes @@ -88,8 +89,8 @@ while [[ $# != 0 ]]; do -a | --autogen ) DO_AUTOGEN=yes ;; - -r | --regenerate-cpp-descriptors ) - REGEN_CPP_DESCRIPTORS=yes + -r | --regenerate-descriptors ) + REGEN_DESCRIPTORS=yes ;; -j | --jobs ) shift @@ -164,8 +165,8 @@ if [[ "${DO_CLEAN}" == "yes" ]] ; then fi fi -if [[ "${REGEN_CPP_DESCRIPTORS}" == "yes" ]] ; then - header "Regenerating the C++ descriptor sources." +if [[ "${REGEN_DESCRIPTORS}" == "yes" ]] ; then + header "Regenerating the descriptor sources." ./generate_descriptor_proto.sh -j "${NUM_MAKE_JOBS}" fi @@ -184,29 +185,8 @@ else cd .. fi -header "Ensuring the ObjC descriptors are current." -# Find the newest input file (protos, compiler, and the generator script). -# (these patterns catch some extra stuff, but better to over sample than under) -readonly NewestInput=$(find \ - src/google/protobuf/*.proto \ - src/.libs src/*.la src/protoc \ - objectivec/generate_descriptors_proto.sh \ - -type f -print0 \ - | xargs -0 stat -f "%m %N" \ - | sort -n | tail -n1 | cut -f2- -d" ") -# Find the oldest output file. -readonly OldestOutput=$(find \ - "${ProtoRootDir}/objectivec/google" \ - -type f -print0 \ - | xargs -0 stat -f "%m %N" \ - | sort -n -r | tail -n1 | cut -f2- -d" ") -# If the newest input is newer than the oldest output, regenerate. -if [[ "${NewestInput}" -nt "${OldestOutput}" ]] ; then - echo ">> Newest input is newer than oldest output, regenerating." - objectivec/generate_descriptors_proto.sh -j "${NUM_MAKE_JOBS}" -else - echo ">> Newest input is older than oldest output, no need to regenerating." -fi +# Ensure the WKT sources checked in are current. +objectivec/generate_well_known_types.sh --check-only -j "${NUM_MAKE_JOBS}" header "Checking on the ObjC Runtime Code" objectivec/DevTools/pddm_tests.py @@ -242,6 +222,14 @@ if [[ "${DO_XCODE_IOS_TESTS}" == "yes" ]] ; then -destination "platform=iOS Simulator,name=iPad Air,OS=9.0" # 64bit ) ;; + 7.3* ) + XCODEBUILD_TEST_BASE_IOS+=( + -destination "platform=iOS Simulator,name=iPhone 4s,OS=8.1" # 32bit + -destination "platform=iOS Simulator,name=iPhone 6,OS=9.3" # 64bit + -destination "platform=iOS Simulator,name=iPad 2,OS=8.1" # 32bit + -destination "platform=iOS Simulator,name=iPad Air,OS=9.3" # 64bit + ) + ;; 7.* ) XCODEBUILD_TEST_BASE_IOS+=( -destination "platform=iOS Simulator,name=iPhone 4s,OS=8.1" # 32bit diff --git a/objectivec/GPBArray.h b/objectivec/GPBArray.h index 8c6396a90..afda57f3b 100644 --- a/objectivec/GPBArray.h +++ b/objectivec/GPBArray.h @@ -53,9 +53,10 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)arrayWithValueArray:(GPBInt32Array *)array; + (instancetype)arrayWithCapacity:(NSUInteger)count; +- (instancetype)init NS_DESIGNATED_INITIALIZER; // Initializes the array, copying the values. - (instancetype)initWithValues:(const int32_t [])values - count:(NSUInteger)count NS_DESIGNATED_INITIALIZER; + count:(NSUInteger)count; - (instancetype)initWithValueArray:(GPBInt32Array *)array; - (instancetype)initWithCapacity:(NSUInteger)count; @@ -92,9 +93,10 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)arrayWithValueArray:(GPBUInt32Array *)array; + (instancetype)arrayWithCapacity:(NSUInteger)count; +- (instancetype)init NS_DESIGNATED_INITIALIZER; // Initializes the array, copying the values. - (instancetype)initWithValues:(const uint32_t [])values - count:(NSUInteger)count NS_DESIGNATED_INITIALIZER; + count:(NSUInteger)count; - (instancetype)initWithValueArray:(GPBUInt32Array *)array; - (instancetype)initWithCapacity:(NSUInteger)count; @@ -131,9 +133,10 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)arrayWithValueArray:(GPBInt64Array *)array; + (instancetype)arrayWithCapacity:(NSUInteger)count; +- (instancetype)init NS_DESIGNATED_INITIALIZER; // Initializes the array, copying the values. - (instancetype)initWithValues:(const int64_t [])values - count:(NSUInteger)count NS_DESIGNATED_INITIALIZER; + count:(NSUInteger)count; - (instancetype)initWithValueArray:(GPBInt64Array *)array; - (instancetype)initWithCapacity:(NSUInteger)count; @@ -170,9 +173,10 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)arrayWithValueArray:(GPBUInt64Array *)array; + (instancetype)arrayWithCapacity:(NSUInteger)count; +- (instancetype)init NS_DESIGNATED_INITIALIZER; // Initializes the array, copying the values. - (instancetype)initWithValues:(const uint64_t [])values - count:(NSUInteger)count NS_DESIGNATED_INITIALIZER; + count:(NSUInteger)count; - (instancetype)initWithValueArray:(GPBUInt64Array *)array; - (instancetype)initWithCapacity:(NSUInteger)count; @@ -209,9 +213,10 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)arrayWithValueArray:(GPBFloatArray *)array; + (instancetype)arrayWithCapacity:(NSUInteger)count; +- (instancetype)init NS_DESIGNATED_INITIALIZER; // Initializes the array, copying the values. - (instancetype)initWithValues:(const float [])values - count:(NSUInteger)count NS_DESIGNATED_INITIALIZER; + count:(NSUInteger)count; - (instancetype)initWithValueArray:(GPBFloatArray *)array; - (instancetype)initWithCapacity:(NSUInteger)count; @@ -248,9 +253,10 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)arrayWithValueArray:(GPBDoubleArray *)array; + (instancetype)arrayWithCapacity:(NSUInteger)count; +- (instancetype)init NS_DESIGNATED_INITIALIZER; // Initializes the array, copying the values. - (instancetype)initWithValues:(const double [])values - count:(NSUInteger)count NS_DESIGNATED_INITIALIZER; + count:(NSUInteger)count; - (instancetype)initWithValueArray:(GPBDoubleArray *)array; - (instancetype)initWithCapacity:(NSUInteger)count; @@ -287,9 +293,10 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)arrayWithValueArray:(GPBBoolArray *)array; + (instancetype)arrayWithCapacity:(NSUInteger)count; +- (instancetype)init NS_DESIGNATED_INITIALIZER; // Initializes the array, copying the values. - (instancetype)initWithValues:(const BOOL [])values - count:(NSUInteger)count NS_DESIGNATED_INITIALIZER; + count:(NSUInteger)count; - (instancetype)initWithValueArray:(GPBBoolArray *)array; - (instancetype)initWithCapacity:(NSUInteger)count; @@ -330,12 +337,13 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)arrayWithValidationFunction:(nullable GPBEnumValidationFunc)func capacity:(NSUInteger)count; -- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func; +- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func + NS_DESIGNATED_INITIALIZER; // Initializes the array, copying the values. - (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func rawValues:(const int32_t [])values - count:(NSUInteger)count NS_DESIGNATED_INITIALIZER; + count:(NSUInteger)count; - (instancetype)initWithValueArray:(GPBEnumArray *)array; - (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func capacity:(NSUInteger)count; @@ -422,9 +430,10 @@ NS_ASSUME_NONNULL_END //%+ (instancetype)arrayWithValueArray:(GPB##NAME##Array *)array; //%+ (instancetype)arrayWithCapacity:(NSUInteger)count; //% +//%- (instancetype)init NS_DESIGNATED_INITIALIZER; //%// Initializes the array, copying the values. //%- (instancetype)initWithValues:(const TYPE [])values -//% count:(NSUInteger)count NS_DESIGNATED_INITIALIZER; +//% count:(NSUInteger)count; //%- (instancetype)initWithValueArray:(GPB##NAME##Array *)array; //%- (instancetype)initWithCapacity:(NSUInteger)count; //% @@ -455,12 +464,13 @@ NS_ASSUME_NONNULL_END //%+ (instancetype)arrayWithValidationFunction:(nullable GPBEnumValidationFunc)func //% capacity:(NSUInteger)count; //% -//%- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func; +//%- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func +//% NS_DESIGNATED_INITIALIZER; //% //%// Initializes the array, copying the values. //%- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func //% rawValues:(const TYPE [])values -//% count:(NSUInteger)count NS_DESIGNATED_INITIALIZER; +//% count:(NSUInteger)count; //%- (instancetype)initWithValueArray:(GPB##NAME##Array *)array; //%- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func //% capacity:(NSUInteger)count; diff --git a/objectivec/GPBArray.m b/objectivec/GPBArray.m index 60b08ad15..426c7cbdd 100644 --- a/objectivec/GPBArray.m +++ b/objectivec/GPBArray.m @@ -75,7 +75,7 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { //%@synthesize count = _count; //% //%+ (instancetype)array { -//% return [[[self alloc] initWithValues:NULL count:0] autorelease]; +//% return [[[self alloc] init] autorelease]; //%} //% //%+ (instancetype)arrayWithValue:(TYPE)value { @@ -93,7 +93,9 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { //%} //% //%- (instancetype)init { -//% return [self initWithValues:NULL count:0]; +//% self = [super init]; +//% // No work needed; +//% return self; //%} //% //%- (instancetype)initWithValueArray:(GPB##NAME##Array *)array { @@ -101,11 +103,11 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { //%} //% //%- (instancetype)initWithValues:(const TYPE [])values count:(NSUInteger)count { -//% self = [super init]; +//% self = [self init]; //% if (self) { //% if (count && values) { -//% _values = malloc(count * sizeof(TYPE)); -//% if (values != NULL) { +//% _values = reallocf(_values, count * sizeof(TYPE)); +//% if (_values != NULL) { //% _capacity = count; //% memcpy(_values, values, count * sizeof(TYPE)); //% _count = count; @@ -299,7 +301,7 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { @synthesize count = _count; + (instancetype)array { - return [[[self alloc] initWithValues:NULL count:0] autorelease]; + return [[[self alloc] init] autorelease]; } + (instancetype)arrayWithValue:(int32_t)value { @@ -317,7 +319,9 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)init { - return [self initWithValues:NULL count:0]; + self = [super init]; + // No work needed; + return self; } - (instancetype)initWithValueArray:(GPBInt32Array *)array { @@ -325,11 +329,11 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)initWithValues:(const int32_t [])values count:(NSUInteger)count { - self = [super init]; + self = [self init]; if (self) { if (count && values) { - _values = malloc(count * sizeof(int32_t)); - if (values != NULL) { + _values = reallocf(_values, count * sizeof(int32_t)); + if (_values != NULL) { _capacity = count; memcpy(_values, values, count * sizeof(int32_t)); _count = count; @@ -544,7 +548,7 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { @synthesize count = _count; + (instancetype)array { - return [[[self alloc] initWithValues:NULL count:0] autorelease]; + return [[[self alloc] init] autorelease]; } + (instancetype)arrayWithValue:(uint32_t)value { @@ -562,7 +566,9 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)init { - return [self initWithValues:NULL count:0]; + self = [super init]; + // No work needed; + return self; } - (instancetype)initWithValueArray:(GPBUInt32Array *)array { @@ -570,11 +576,11 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)initWithValues:(const uint32_t [])values count:(NSUInteger)count { - self = [super init]; + self = [self init]; if (self) { if (count && values) { - _values = malloc(count * sizeof(uint32_t)); - if (values != NULL) { + _values = reallocf(_values, count * sizeof(uint32_t)); + if (_values != NULL) { _capacity = count; memcpy(_values, values, count * sizeof(uint32_t)); _count = count; @@ -789,7 +795,7 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { @synthesize count = _count; + (instancetype)array { - return [[[self alloc] initWithValues:NULL count:0] autorelease]; + return [[[self alloc] init] autorelease]; } + (instancetype)arrayWithValue:(int64_t)value { @@ -807,7 +813,9 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)init { - return [self initWithValues:NULL count:0]; + self = [super init]; + // No work needed; + return self; } - (instancetype)initWithValueArray:(GPBInt64Array *)array { @@ -815,11 +823,11 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)initWithValues:(const int64_t [])values count:(NSUInteger)count { - self = [super init]; + self = [self init]; if (self) { if (count && values) { - _values = malloc(count * sizeof(int64_t)); - if (values != NULL) { + _values = reallocf(_values, count * sizeof(int64_t)); + if (_values != NULL) { _capacity = count; memcpy(_values, values, count * sizeof(int64_t)); _count = count; @@ -1034,7 +1042,7 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { @synthesize count = _count; + (instancetype)array { - return [[[self alloc] initWithValues:NULL count:0] autorelease]; + return [[[self alloc] init] autorelease]; } + (instancetype)arrayWithValue:(uint64_t)value { @@ -1052,7 +1060,9 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)init { - return [self initWithValues:NULL count:0]; + self = [super init]; + // No work needed; + return self; } - (instancetype)initWithValueArray:(GPBUInt64Array *)array { @@ -1060,11 +1070,11 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)initWithValues:(const uint64_t [])values count:(NSUInteger)count { - self = [super init]; + self = [self init]; if (self) { if (count && values) { - _values = malloc(count * sizeof(uint64_t)); - if (values != NULL) { + _values = reallocf(_values, count * sizeof(uint64_t)); + if (_values != NULL) { _capacity = count; memcpy(_values, values, count * sizeof(uint64_t)); _count = count; @@ -1279,7 +1289,7 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { @synthesize count = _count; + (instancetype)array { - return [[[self alloc] initWithValues:NULL count:0] autorelease]; + return [[[self alloc] init] autorelease]; } + (instancetype)arrayWithValue:(float)value { @@ -1297,7 +1307,9 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)init { - return [self initWithValues:NULL count:0]; + self = [super init]; + // No work needed; + return self; } - (instancetype)initWithValueArray:(GPBFloatArray *)array { @@ -1305,11 +1317,11 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)initWithValues:(const float [])values count:(NSUInteger)count { - self = [super init]; + self = [self init]; if (self) { if (count && values) { - _values = malloc(count * sizeof(float)); - if (values != NULL) { + _values = reallocf(_values, count * sizeof(float)); + if (_values != NULL) { _capacity = count; memcpy(_values, values, count * sizeof(float)); _count = count; @@ -1524,7 +1536,7 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { @synthesize count = _count; + (instancetype)array { - return [[[self alloc] initWithValues:NULL count:0] autorelease]; + return [[[self alloc] init] autorelease]; } + (instancetype)arrayWithValue:(double)value { @@ -1542,7 +1554,9 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)init { - return [self initWithValues:NULL count:0]; + self = [super init]; + // No work needed; + return self; } - (instancetype)initWithValueArray:(GPBDoubleArray *)array { @@ -1550,11 +1564,11 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)initWithValues:(const double [])values count:(NSUInteger)count { - self = [super init]; + self = [self init]; if (self) { if (count && values) { - _values = malloc(count * sizeof(double)); - if (values != NULL) { + _values = reallocf(_values, count * sizeof(double)); + if (_values != NULL) { _capacity = count; memcpy(_values, values, count * sizeof(double)); _count = count; @@ -1769,7 +1783,7 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { @synthesize count = _count; + (instancetype)array { - return [[[self alloc] initWithValues:NULL count:0] autorelease]; + return [[[self alloc] init] autorelease]; } + (instancetype)arrayWithValue:(BOOL)value { @@ -1787,7 +1801,9 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)init { - return [self initWithValues:NULL count:0]; + self = [super init]; + // No work needed; + return self; } - (instancetype)initWithValueArray:(GPBBoolArray *)array { @@ -1795,11 +1811,11 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)initWithValues:(const BOOL [])values count:(NSUInteger)count { - self = [super init]; + self = [self init]; if (self) { if (count && values) { - _values = malloc(count * sizeof(BOOL)); - if (values != NULL) { + _values = reallocf(_values, count * sizeof(BOOL)); + if (_values != NULL) { _capacity = count; memcpy(_values, values, count * sizeof(BOOL)); _count = count; @@ -2015,15 +2031,11 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { @synthesize validationFunc = _validationFunc; + (instancetype)array { - return [[[self alloc] initWithValidationFunction:NULL - rawValues:NULL - count:0] autorelease]; + return [[[self alloc] initWithValidationFunction:NULL] autorelease]; } + (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func { - return [[[self alloc] initWithValidationFunction:func - rawValues:NULL - count:0] autorelease]; + return [[[self alloc] initWithValidationFunction:func] autorelease]; } + (instancetype)arrayWithValidationFunction:(GPBEnumValidationFunc)func @@ -2043,7 +2055,7 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)init { - return [self initWithValidationFunction:NULL rawValues:NULL count:0]; + return [self initWithValidationFunction:NULL]; } - (instancetype)initWithValueArray:(GPBEnumArray *)array { @@ -2053,18 +2065,21 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { } - (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func { - return [self initWithValidationFunction:func rawValues:NULL count:0]; + self = [super init]; + if (self) { + _validationFunc = (func != NULL ? func : ArrayDefault_IsValidValue); + } + return self; } - (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func rawValues:(const int32_t [])values count:(NSUInteger)count { - self = [super init]; + self = [self initWithValidationFunction:func]; if (self) { - _validationFunc = (func != NULL ? func : ArrayDefault_IsValidValue); if (count && values) { - _values = malloc(count * sizeof(int32_t)); - if (values != NULL) { + _values = reallocf(_values, count * sizeof(int32_t)); + if (_values != NULL) { _capacity = count; memcpy(_values, values, count * sizeof(int32_t)); _count = count; @@ -2081,7 +2096,7 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) { - (instancetype)initWithValidationFunction:(GPBEnumValidationFunc)func capacity:(NSUInteger)count { - self = [self initWithValidationFunction:func rawValues:NULL count:0]; + self = [self initWithValidationFunction:func]; if (self && count) { [self internalResizeToCapacity:count]; } diff --git a/objectivec/GPBBootstrap.h b/objectivec/GPBBootstrap.h index c49c7e20e..ffefa7706 100644 --- a/objectivec/GPBBootstrap.h +++ b/objectivec/GPBBootstrap.h @@ -37,13 +37,6 @@ #define GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS 0 #endif -// Most uses of protocol buffers don't need field options, by default the -// static data will be compiled out, define this to 1 to include it. The only -// time you need this is if you are doing introspection of the protocol buffers. -#ifndef GPBOBJC_INCLUDE_FIELD_OPTIONS -#define GPBOBJC_INCLUDE_FIELD_OPTIONS 0 -#endif - // Used in the generated code to give sizes to enums. int32_t was chosen based // on the fact that Protocol Buffers enums are limited to this range. #if !__has_feature(objc_fixed_enum) @@ -89,4 +82,4 @@ // generated Objective C sources. In general we don't want to change the // runtime interfaces (or this version) as it means everything has to be // regenerated. -#define GOOGLE_PROTOBUF_OBJC_GEN_VERSION 30000 +#define GOOGLE_PROTOBUF_OBJC_GEN_VERSION 30001 diff --git a/objectivec/GPBCodedInputStream.h b/objectivec/GPBCodedInputStream.h index 061988832..d64b64e3f 100644 --- a/objectivec/GPBCodedInputStream.h +++ b/objectivec/GPBCodedInputStream.h @@ -109,6 +109,15 @@ NS_ASSUME_NONNULL_BEGIN /// or until an endgroup tag, whichever comes first. - (void)skipMessage; +/// Check to see if the logical end of the stream has been reached. +/// +/// This can return NO when there is no more data, but the current parsing +/// expected more data. +- (BOOL)isAtEnd; + +/// The offset into the stream. +- (size_t)position; + /// Verifies that the last call to @c -readTag returned the given tag value. /// This is used to verify that a nested group ended with the correct end tag. /// Throws @c NSParseErrorException if value does not match the last tag. diff --git a/objectivec/GPBCodedInputStream.m b/objectivec/GPBCodedInputStream.m index fd8778383..319ec15ba 100644 --- a/objectivec/GPBCodedInputStream.m +++ b/objectivec/GPBCodedInputStream.m @@ -219,15 +219,16 @@ NSString *GPBCodedInputStreamReadRetainedString( result = [[NSString alloc] initWithBytes:&state->bytes[state->bufferPos] length:size encoding:NSUTF8StringEncoding]; + state->bufferPos += size; if (!result) { - result = @""; #ifdef DEBUG // https://developers.google.com/protocol-buffers/docs/proto#scalar - NSLog(@"UTF8 failure, is some field type 'string' when it should be " + NSLog(@"UTF-8 failure, is some field type 'string' when it should be " @"'bytes'?"); #endif + [NSException raise:NSParseErrorException + format:@"Invalid UTF-8 for a 'string'"]; } - state->bufferPos += size; } return result; } @@ -359,6 +360,14 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, } } +- (BOOL)isAtEnd { + return GPBCodedInputStreamIsAtEnd(&state_); +} + +- (size_t)position { + return state_.bufferPos; +} + - (double)readDouble { return GPBCodedInputStreamReadDouble(&state_); } diff --git a/objectivec/GPBCodedOutputStream.h b/objectivec/GPBCodedOutputStream.h index e67efd2d4..8272880df 100644 --- a/objectivec/GPBCodedOutputStream.h +++ b/objectivec/GPBCodedOutputStream.h @@ -112,7 +112,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a double for the given field number. - (void)writeDouble:(int32_t)fieldNumber value:(double)value; -/// Write a packaged array of double for the given field number. +/// Write a packed array of double for the given field number. - (void)writeDoubleArray:(int32_t)fieldNumber values:(GPBDoubleArray *)values tag:(uint32_t)tag; @@ -121,7 +121,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a float for the given field number. - (void)writeFloat:(int32_t)fieldNumber value:(float)value; -/// Write a packaged array of float for the given field number. +/// Write a packed array of float for the given field number. - (void)writeFloatArray:(int32_t)fieldNumber values:(GPBFloatArray *)values tag:(uint32_t)tag; @@ -130,7 +130,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a uint64_t for the given field number. - (void)writeUInt64:(int32_t)fieldNumber value:(uint64_t)value; -/// Write a packaged array of uint64_t for the given field number. +/// Write a packed array of uint64_t for the given field number. - (void)writeUInt64Array:(int32_t)fieldNumber values:(GPBUInt64Array *)values tag:(uint32_t)tag; @@ -139,7 +139,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a int64_t for the given field number. - (void)writeInt64:(int32_t)fieldNumber value:(int64_t)value; -/// Write a packaged array of int64_t for the given field number. +/// Write a packed array of int64_t for the given field number. - (void)writeInt64Array:(int32_t)fieldNumber values:(GPBInt64Array *)values tag:(uint32_t)tag; @@ -148,7 +148,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a int32_t for the given field number. - (void)writeInt32:(int32_t)fieldNumber value:(int32_t)value; -/// Write a packaged array of int32_t for the given field number. +/// Write a packed array of int32_t for the given field number. - (void)writeInt32Array:(int32_t)fieldNumber values:(GPBInt32Array *)values tag:(uint32_t)tag; @@ -157,7 +157,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a uint32_t for the given field number. - (void)writeUInt32:(int32_t)fieldNumber value:(uint32_t)value; -/// Write a packaged array of uint32_t for the given field number. +/// Write a packed array of uint32_t for the given field number. - (void)writeUInt32Array:(int32_t)fieldNumber values:(GPBUInt32Array *)values tag:(uint32_t)tag; @@ -166,7 +166,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a uint64_t for the given field number. - (void)writeFixed64:(int32_t)fieldNumber value:(uint64_t)value; -/// Write a packaged array of uint64_t for the given field number. +/// Write a packed array of uint64_t for the given field number. - (void)writeFixed64Array:(int32_t)fieldNumber values:(GPBUInt64Array *)values tag:(uint32_t)tag; @@ -175,7 +175,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a uint32_t for the given field number. - (void)writeFixed32:(int32_t)fieldNumber value:(uint32_t)value; -/// Write a packaged array of uint32_t for the given field number. +/// Write a packed array of uint32_t for the given field number. - (void)writeFixed32Array:(int32_t)fieldNumber values:(GPBUInt32Array *)values tag:(uint32_t)tag; @@ -184,7 +184,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a int32_t for the given field number. - (void)writeSInt32:(int32_t)fieldNumber value:(int32_t)value; -/// Write a packaged array of int32_t for the given field number. +/// Write a packed array of int32_t for the given field number. - (void)writeSInt32Array:(int32_t)fieldNumber values:(GPBInt32Array *)values tag:(uint32_t)tag; @@ -193,7 +193,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a int64_t for the given field number. - (void)writeSInt64:(int32_t)fieldNumber value:(int64_t)value; -/// Write a packaged array of int64_t for the given field number. +/// Write a packed array of int64_t for the given field number. - (void)writeSInt64Array:(int32_t)fieldNumber values:(GPBInt64Array *)values tag:(uint32_t)tag; @@ -202,7 +202,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a int64_t for the given field number. - (void)writeSFixed64:(int32_t)fieldNumber value:(int64_t)value; -/// Write a packaged array of int64_t for the given field number. +/// Write a packed array of int64_t for the given field number. - (void)writeSFixed64Array:(int32_t)fieldNumber values:(GPBInt64Array *)values tag:(uint32_t)tag; @@ -211,7 +211,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a int32_t for the given field number. - (void)writeSFixed32:(int32_t)fieldNumber value:(int32_t)value; -/// Write a packaged array of int32_t for the given field number. +/// Write a packed array of int32_t for the given field number. - (void)writeSFixed32Array:(int32_t)fieldNumber values:(GPBInt32Array *)values tag:(uint32_t)tag; @@ -220,7 +220,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a BOOL for the given field number. - (void)writeBool:(int32_t)fieldNumber value:(BOOL)value; -/// Write a packaged array of BOOL for the given field number. +/// Write a packed array of BOOL for the given field number. - (void)writeBoolArray:(int32_t)fieldNumber values:(GPBBoolArray *)values tag:(uint32_t)tag; @@ -229,7 +229,7 @@ NS_ASSUME_NONNULL_BEGIN /// Write a int32_t for the given field number. - (void)writeEnum:(int32_t)fieldNumber value:(int32_t)value; -/// Write a packaged array of int32_t for the given field number. +/// Write a packed array of int32_t for the given field number. - (void)writeEnumArray:(int32_t)fieldNumber values:(GPBEnumArray *)values tag:(uint32_t)tag; @@ -293,7 +293,7 @@ NS_ASSUME_NONNULL_END //%PDDM-DEFINE _WRITE_PACKABLE_DECLS(NAME, ARRAY_TYPE, TYPE) //%/// Write a TYPE for the given field number. //%- (void)write##NAME:(int32_t)fieldNumber value:(TYPE)value; -//%/// Write a packaged array of TYPE for the given field number. +//%/// Write a packed array of TYPE for the given field number. //%- (void)write##NAME##Array:(int32_t)fieldNumber //% NAME$S values:(GPB##ARRAY_TYPE##Array *)values //% NAME$S tag:(uint32_t)tag; diff --git a/objectivec/GPBDescriptor.h b/objectivec/GPBDescriptor.h index 8d8e97540..a6eff0fb7 100644 --- a/objectivec/GPBDescriptor.h +++ b/objectivec/GPBDescriptor.h @@ -34,19 +34,18 @@ @class GPBEnumDescriptor; @class GPBFieldDescriptor; -@class GPBFieldOptions; @class GPBFileDescriptor; @class GPBOneofDescriptor; NS_ASSUME_NONNULL_BEGIN -typedef NS_ENUM(NSInteger, GPBFileSyntax) { +typedef NS_ENUM(uint8_t, GPBFileSyntax) { GPBFileSyntaxUnknown = 0, GPBFileSyntaxProto2 = 2, GPBFileSyntaxProto3 = 3, }; -typedef NS_ENUM(NSInteger, GPBFieldType) { +typedef NS_ENUM(uint8_t, GPBFieldType) { GPBFieldTypeSingle, // optional/required GPBFieldTypeRepeated, // repeated GPBFieldTypeMap, // map @@ -57,9 +56,8 @@ typedef NS_ENUM(NSInteger, GPBFieldType) { @property(nonatomic, readonly, copy) NSString *name; @property(nonatomic, readonly, strong, nullable) NSArray *fields; @property(nonatomic, readonly, strong, nullable) NSArray *oneofs; -@property(nonatomic, readonly, strong, nullable) NSArray *enums; @property(nonatomic, readonly, nullable) const GPBExtensionRange *extensionRanges; -@property(nonatomic, readonly) NSUInteger extensionRangesCount; +@property(nonatomic, readonly) uint32_t extensionRangesCount; @property(nonatomic, readonly, assign) GPBFileDescriptor *file; @property(nonatomic, readonly, getter=isWireFormat) BOOL wireFormat; @@ -68,7 +66,6 @@ typedef NS_ENUM(NSInteger, GPBFieldType) { - (nullable GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber; - (nullable GPBFieldDescriptor *)fieldWithName:(NSString *)name; - (nullable GPBOneofDescriptor *)oneofWithName:(NSString *)name; -- (nullable GPBEnumDescriptor *)enumWithName:(NSString *)name; @end @@ -103,8 +100,6 @@ typedef NS_ENUM(NSInteger, GPBFieldType) { @property(nonatomic, readonly, assign, nullable) GPBOneofDescriptor *containingOneof; -@property(nonatomic, readonly, nullable) GPBFieldOptions *fieldOptions; - // Message properties @property(nonatomic, readonly, assign, nullable) Class msgClass; diff --git a/objectivec/GPBDescriptor.m b/objectivec/GPBDescriptor.m index bae9187ee..2709737ca 100644 --- a/objectivec/GPBDescriptor.m +++ b/objectivec/GPBDescriptor.m @@ -35,7 +35,6 @@ #import "GPBUtilities_PackagePrivate.h" #import "GPBWireFormat.h" #import "GPBMessage_PackagePrivate.h" -#import "google/protobuf/Descriptor.pbobjc.h" // The address of this variable is used as a key for obj_getAssociatedObject. static const char kTextFormatExtraValueKey = 0; @@ -92,7 +91,6 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, @implementation GPBDescriptor { Class messageClass_; - NSArray *enums_; GPBFileDescriptor *file_; BOOL wireFormat_; } @@ -100,7 +98,6 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, @synthesize messageClass = messageClass_; @synthesize fields = fields_; @synthesize oneofs = oneofs_; -@synthesize enums = enums_; @synthesize extensionRanges = extensionRanges_; @synthesize extensionRangesCount = extensionRangesCount_; @synthesize file = file_; @@ -110,130 +107,58 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, allocDescriptorForClass:(Class)messageClass rootClass:(Class)rootClass file:(GPBFileDescriptor *)file - fields:(GPBMessageFieldDescription *)fieldDescriptions - fieldCount:(NSUInteger)fieldCount - oneofs:(GPBMessageOneofDescription *)oneofDescriptions - oneofCount:(NSUInteger)oneofCount - enums:(GPBMessageEnumDescription *)enumDescriptions - enumCount:(NSUInteger)enumCount - ranges:(const GPBExtensionRange *)ranges - rangeCount:(NSUInteger)rangeCount - storageSize:(size_t)storageSize - wireFormat:(BOOL)wireFormat { + fields:(void *)fieldDescriptions + fieldCount:(uint32_t)fieldCount + storageSize:(uint32_t)storageSize + flags:(GPBDescriptorInitializationFlags)flags { + // The rootClass is no longer used, but it is passed in to ensure it + // was started up during initialization also. + (void)rootClass; NSMutableArray *fields = nil; - NSMutableArray *oneofs = nil; - NSMutableArray *enums = nil; - NSMutableArray *extensionRanges = nil; GPBFileSyntax syntax = file.syntax; - for (NSUInteger i = 0; i < fieldCount; ++i) { + BOOL fieldsIncludeDefault = + (flags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0; + + void *desc; + for (uint32_t i = 0; i < fieldCount; ++i) { if (fields == nil) { fields = [[NSMutableArray alloc] initWithCapacity:fieldCount]; } - GPBFieldDescriptor *fieldDescriptor = [[GPBFieldDescriptor alloc] - initWithFieldDescription:&fieldDescriptions[i] - rootClass:rootClass - syntax:syntax]; + // Need correctly typed pointer for array indexing below to work. + if (fieldsIncludeDefault) { + GPBMessageFieldDescriptionWithDefault *fieldDescWithDefault = fieldDescriptions; + desc = &(fieldDescWithDefault[i]); + } else { + GPBMessageFieldDescription *fieldDesc = fieldDescriptions; + desc = &(fieldDesc[i]); + } + GPBFieldDescriptor *fieldDescriptor = + [[GPBFieldDescriptor alloc] initWithFieldDescription:desc + includesDefault:fieldsIncludeDefault + syntax:syntax]; [fields addObject:fieldDescriptor]; [fieldDescriptor release]; } - for (NSUInteger i = 0; i < oneofCount; ++i) { - if (oneofs == nil) { - oneofs = [[NSMutableArray alloc] initWithCapacity:oneofCount]; - } - GPBMessageOneofDescription *oneofDescription = &oneofDescriptions[i]; - NSArray *fieldsForOneof = - NewFieldsArrayForHasIndex(oneofDescription->index, fields); - GPBOneofDescriptor *oneofDescriptor = - [[GPBOneofDescriptor alloc] initWithOneofDescription:oneofDescription - fields:fieldsForOneof]; - [oneofs addObject:oneofDescriptor]; - [oneofDescriptor release]; - [fieldsForOneof release]; - } - for (NSUInteger i = 0; i < enumCount; ++i) { - if (enums == nil) { - enums = [[NSMutableArray alloc] initWithCapacity:enumCount]; - } - GPBEnumDescriptor *enumDescriptor = - enumDescriptions[i].enumDescriptorFunc(); - [enums addObject:enumDescriptor]; - } + BOOL wireFormat = (flags & GPBDescriptorInitializationFlag_WireFormat) != 0; GPBDescriptor *descriptor = [[self alloc] initWithClass:messageClass file:file fields:fields - oneofs:oneofs - enums:enums - extensionRanges:ranges - extensionRangesCount:rangeCount storageSize:storageSize wireFormat:wireFormat]; [fields release]; - [oneofs release]; - [enums release]; - [extensionRanges release]; - return descriptor; -} - -+ (instancetype) - allocDescriptorForClass:(Class)messageClass - rootClass:(Class)rootClass - file:(GPBFileDescriptor *)file - fields:(GPBMessageFieldDescription *)fieldDescriptions - fieldCount:(NSUInteger)fieldCount - oneofs:(GPBMessageOneofDescription *)oneofDescriptions - oneofCount:(NSUInteger)oneofCount - enums:(GPBMessageEnumDescription *)enumDescriptions - enumCount:(NSUInteger)enumCount - ranges:(const GPBExtensionRange *)ranges - rangeCount:(NSUInteger)rangeCount - storageSize:(size_t)storageSize - wireFormat:(BOOL)wireFormat - extraTextFormatInfo:(const char *)extraTextFormatInfo { - GPBDescriptor *descriptor = [self allocDescriptorForClass:messageClass - rootClass:rootClass - file:file - fields:fieldDescriptions - fieldCount:fieldCount - oneofs:oneofDescriptions - oneofCount:oneofCount - enums:enumDescriptions - enumCount:enumCount - ranges:ranges - rangeCount:rangeCount - storageSize:storageSize - wireFormat:wireFormat]; - // Extra info is a compile time option, so skip the work if not needed. - if (extraTextFormatInfo) { - NSValue *extraInfoValue = [NSValue valueWithPointer:extraTextFormatInfo]; - for (GPBFieldDescriptor *fieldDescriptor in descriptor->fields_) { - if (fieldDescriptor->description_->flags & GPBFieldTextFormatNameCustom) { - objc_setAssociatedObject(fieldDescriptor, &kTextFormatExtraValueKey, - extraInfoValue, - OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - } - } return descriptor; } - (instancetype)initWithClass:(Class)messageClass file:(GPBFileDescriptor *)file fields:(NSArray *)fields - oneofs:(NSArray *)oneofs - enums:(NSArray *)enums - extensionRanges:(const GPBExtensionRange *)extensionRanges - extensionRangesCount:(NSUInteger)extensionRangesCount - storageSize:(size_t)storageSize + storageSize:(uint32_t)storageSize wireFormat:(BOOL)wireFormat { if ((self = [super init])) { messageClass_ = messageClass; file_ = file; fields_ = [fields retain]; - oneofs_ = [oneofs retain]; - enums_ = [enums retain]; - extensionRanges_ = extensionRanges; - extensionRangesCount_ = extensionRangesCount; storageSize_ = storageSize; wireFormat_ = wireFormat; } @@ -243,10 +168,47 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, - (void)dealloc { [fields_ release]; [oneofs_ release]; - [enums_ release]; [super dealloc]; } +- (void)setupOneofs:(const char **)oneofNames + count:(uint32_t)count + firstHasIndex:(int32_t)firstHasIndex { + NSCAssert(firstHasIndex < 0, @"Should always be <0"); + NSMutableArray *oneofs = [[NSMutableArray alloc] initWithCapacity:count]; + for (uint32_t i = 0, hasIndex = firstHasIndex; i < count; ++i, --hasIndex) { + const char *name = oneofNames[i]; + NSArray *fieldsForOneof = NewFieldsArrayForHasIndex(hasIndex, fields_); + NSCAssert(fieldsForOneof.count > 0, + @"No fields for this oneof? (%s:%d)", name, hasIndex); + GPBOneofDescriptor *oneofDescriptor = + [[GPBOneofDescriptor alloc] initWithName:name fields:fieldsForOneof]; + [oneofs addObject:oneofDescriptor]; + [oneofDescriptor release]; + [fieldsForOneof release]; + } + oneofs_ = oneofs; +} + +- (void)setupExtraTextInfo:(const char *)extraTextFormatInfo { + // Extra info is a compile time option, so skip the work if not needed. + if (extraTextFormatInfo) { + NSValue *extraInfoValue = [NSValue valueWithPointer:extraTextFormatInfo]; + for (GPBFieldDescriptor *fieldDescriptor in fields_) { + if (fieldDescriptor->description_->flags & GPBFieldTextFormatNameCustom) { + objc_setAssociatedObject(fieldDescriptor, &kTextFormatExtraValueKey, + extraInfoValue, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + } + } +} + +- (void)setupExtensionRanges:(const GPBExtensionRange *)ranges count:(int32_t)count { + extensionRanges_ = ranges; + extensionRangesCount_ = count; +} + - (NSString *)name { return NSStringFromClass(messageClass_); } @@ -283,15 +245,6 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, return nil; } -- (GPBEnumDescriptor *)enumWithName:(NSString *)name { - for (GPBEnumDescriptor *descriptor in enums_) { - if ([descriptor.name isEqual:name]) { - return descriptor; - } - } - return nil; -} - @end @implementation GPBFileDescriptor { @@ -318,19 +271,16 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, @synthesize fields = fields_; -- (instancetype)initWithOneofDescription: - (GPBMessageOneofDescription *)oneofDescription - fields:(NSArray *)fields { +- (instancetype)initWithName:(const char *)name fields:(NSArray *)fields { self = [super init]; if (self) { - NSAssert(oneofDescription->index < 0, @"Should always be <0"); - oneofDescription_ = oneofDescription; + name_ = name; fields_ = [fields retain]; for (GPBFieldDescriptor *fieldDesc in fields) { fieldDesc->containingOneof_ = self; } - caseSel_ = SelFromStrings(NULL, oneofDescription->name, "OneOfCase", NO); + caseSel_ = SelFromStrings(NULL, name, "OneOfCase", NO); } return self; } @@ -341,7 +291,7 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, } - (NSString *)name { - return @(oneofDescription_->name); + return @(name_); } - (GPBFieldDescriptor *)fieldWithNumber:(uint32_t)fieldNumber { @@ -389,7 +339,6 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { @implementation GPBFieldDescriptor { GPBGenericValue defaultValue_; - GPBFieldOptions *fieldOptions_; // Message ivars Class msgClass_; @@ -403,7 +352,6 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { } enumHandling_; } -@synthesize fieldOptions = fieldOptions_; @synthesize msgClass = msgClass_; @synthesize containingOneof = containingOneof_; @@ -417,16 +365,21 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { return self; } -- (instancetype)initWithFieldDescription: - (GPBMessageFieldDescription *)description - rootClass:(Class)rootClass +- (instancetype)initWithFieldDescription:(void *)description + includesDefault:(BOOL)includesDefault syntax:(GPBFileSyntax)syntax { if ((self = [super init])) { - description_ = description; - getSel_ = sel_getUid(description->name); - setSel_ = SelFromStrings("set", description->name, NULL, YES); + GPBMessageFieldDescription *coreDesc; + if (includesDefault) { + coreDesc = &(((GPBMessageFieldDescriptionWithDefault *)description)->core); + } else { + coreDesc = description; + } + description_ = coreDesc; + getSel_ = sel_getUid(coreDesc->name); + setSel_ = SelFromStrings("set", coreDesc->name, NULL, YES); - GPBDataType dataType = description->dataType; + GPBDataType dataType = coreDesc->dataType; BOOL isMessage = GPBDataTypeIsMessage(dataType); BOOL isMapOrArray = GPBFieldIsMapOrArray(self); @@ -434,39 +387,39 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { // map<>/repeated fields get a *Count property (inplace of a has*) to // support checking if there are any entries without triggering // autocreation. - hasOrCountSel_ = SelFromStrings(NULL, description->name, "_Count", NO); + hasOrCountSel_ = SelFromStrings(NULL, coreDesc->name, "_Count", NO); } else { // If there is a positive hasIndex, then: // - All fields types for proto2 messages get has* selectors. // - Only message fields for proto3 messages get has* selectors. // Note: the positive check is to handle oneOfs, we can't check // containingOneof_ because it isn't set until after initialization. - if ((description->hasIndex >= 0) && - (description->hasIndex != GPBNoHasBit) && + if ((coreDesc->hasIndex >= 0) && + (coreDesc->hasIndex != GPBNoHasBit) && ((syntax != GPBFileSyntaxProto3) || isMessage)) { - hasOrCountSel_ = SelFromStrings("has", description->name, NULL, NO); - setHasSel_ = SelFromStrings("setHas", description->name, NULL, YES); + hasOrCountSel_ = SelFromStrings("has", coreDesc->name, NULL, NO); + setHasSel_ = SelFromStrings("setHas", coreDesc->name, NULL, YES); } } // Extra type specific data. if (isMessage) { - const char *className = description->dataTypeSpecific.className; + const char *className = coreDesc->dataTypeSpecific.className; msgClass_ = objc_getClass(className); NSAssert(msgClass_, @"Class %s not defined", className); } else if (dataType == GPBDataTypeEnum) { - if ((description_->flags & GPBFieldHasEnumDescriptor) != 0) { + if ((coreDesc->flags & GPBFieldHasEnumDescriptor) != 0) { enumHandling_.enumDescriptor_ = - description->dataTypeSpecific.enumDescFunc(); + coreDesc->dataTypeSpecific.enumDescFunc(); } else { enumHandling_.enumVerifier_ = - description->dataTypeSpecific.enumVerifier; + coreDesc->dataTypeSpecific.enumVerifier; } } - // Non map<>/repeated fields can have defaults. - if (!isMapOrArray) { - defaultValue_ = description->defaultValue; + // Non map<>/repeated fields can have defaults in proto2 syntax. + if (!isMapOrArray && includesDefault) { + defaultValue_ = ((GPBMessageFieldDescriptionWithDefault *)description)->defaultValue; if (dataType == GPBDataTypeBytes) { // Data stored as a length prefixed (network byte order) c-string in // descriptor structure. @@ -480,24 +433,6 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { } } } - - // FieldOptions stored as a length prefixed (network byte order) c-escaped - // string in descriptor records. - if (description->fieldOptions) { - uint8_t *optionsBytes = (uint8_t *)description->fieldOptions; - uint32_t optionsLength = *((uint32_t *)optionsBytes); - optionsLength = ntohl(optionsLength); - if (optionsLength > 0) { - optionsBytes += sizeof(optionsLength); - NSData *optionsData = [NSData dataWithBytesNoCopy:optionsBytes - length:optionsLength - freeWhenDone:NO]; - GPBExtensionRegistry *registry = [rootClass extensionRegistry]; - fieldOptions_ = [[GPBFieldOptions parseFromData:optionsData - extensionRegistry:registry - error:NULL] retain]; - } - } } return self; } @@ -666,7 +601,7 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { } else { // Undo the CamelCase. NSMutableString *result = [NSMutableString stringWithCapacity:len]; - for (NSUInteger i = 0; i < len; i++) { + for (uint32_t i = 0; i < len; i++) { unichar c = [name characterAtIndex:i]; if (c >= 'A' && c <= 'Z') { if (i > 0) { @@ -686,10 +621,16 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { @implementation GPBEnumDescriptor { NSString *name_; - GPBMessageEnumValueDescription *valueDescriptions_; - NSUInteger valueDescriptionsCount_; + // valueNames_ is a single c string with all of the value names appended + // together, each null terminated. -calcValueNameOffsets fills in + // nameOffsets_ with the offsets to allow quicker access to the individual + // names. + const char *valueNames_; + const int32_t *values_; GPBEnumValidationFunc enumVerifier_; const uint8_t *extraTextFormatInfo_; + uint32_t *nameOffsets_; + uint32_t valueCount_; } @synthesize name = name_; @@ -697,26 +638,30 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { + (instancetype) allocDescriptorForName:(NSString *)name - values:(GPBMessageEnumValueDescription *)valueDescriptions - valueCount:(NSUInteger)valueCount + valueNames:(const char *)valueNames + values:(const int32_t *)values + count:(uint32_t)valueCount enumVerifier:(GPBEnumValidationFunc)enumVerifier { GPBEnumDescriptor *descriptor = [[self alloc] initWithName:name - values:valueDescriptions - valueCount:valueCount + valueNames:valueNames + values:values + count:valueCount enumVerifier:enumVerifier]; return descriptor; } + (instancetype) allocDescriptorForName:(NSString *)name - values:(GPBMessageEnumValueDescription *)valueDescriptions - valueCount:(NSUInteger)valueCount + valueNames:(const char *)valueNames + values:(const int32_t *)values + count:(uint32_t)valueCount enumVerifier:(GPBEnumValidationFunc)enumVerifier extraTextFormatInfo:(const char *)extraTextFormatInfo { // Call the common case. GPBEnumDescriptor *descriptor = [self allocDescriptorForName:name - values:valueDescriptions - valueCount:valueCount + valueNames:valueNames + values:values + count:valueCount enumVerifier:enumVerifier]; // Set the extra info. descriptor->extraTextFormatInfo_ = (const uint8_t *)extraTextFormatInfo; @@ -724,24 +669,49 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { } - (instancetype)initWithName:(NSString *)name - values:(GPBMessageEnumValueDescription *)valueDescriptions - valueCount:(NSUInteger)valueCount + valueNames:(const char *)valueNames + values:(const int32_t *)values + count:(uint32_t)valueCount enumVerifier:(GPBEnumValidationFunc)enumVerifier { if ((self = [super init])) { name_ = [name copy]; - valueDescriptions_ = valueDescriptions; - valueDescriptionsCount_ = valueCount; + valueNames_ = valueNames; + values_ = values; + valueCount_ = valueCount; enumVerifier_ = enumVerifier; } return self; } +- (void)dealloc { + [name_ release]; + if (nameOffsets_) free(nameOffsets_); + [super dealloc]; +} + +- (void)calcValueNameOffsets { + @synchronized(self) { + if (nameOffsets_ != NULL) { + return; + } + uint32_t *offsets = malloc(valueCount_ * sizeof(uint32_t)); + const char *scan = valueNames_; + for (uint32_t i = 0; i < valueCount_; ++i) { + offsets[i] = (uint32_t)(scan - valueNames_); + while (*scan != '\0') ++scan; + ++scan; // Step over the null. + } + nameOffsets_ = offsets; + } +} + - (NSString *)enumNameForValue:(int32_t)number { - for (NSUInteger i = 0; i < valueDescriptionsCount_; ++i) { - GPBMessageEnumValueDescription *scan = &valueDescriptions_[i]; - if ((scan->number == number) && (scan->name != NULL)) { - NSString *fullName = - [NSString stringWithFormat:@"%@_%s", name_, scan->name]; + if (nameOffsets_ == NULL) [self calcValueNameOffsets]; + + for (uint32_t i = 0; i < valueCount_; ++i) { + if (values_[i] == number) { + const char *valueName = valueNames_ + nameOffsets_[i]; + NSString *fullName = [NSString stringWithFormat:@"%@_%s", name_, valueName]; return fullName; } } @@ -760,12 +730,14 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { const char *nameAsCStr = [name UTF8String]; nameAsCStr += prefixLen; + if (nameOffsets_ == NULL) [self calcValueNameOffsets]; + // Find it. - for (NSUInteger i = 0; i < valueDescriptionsCount_; ++i) { - GPBMessageEnumValueDescription *scan = &valueDescriptions_[i]; - if ((scan->name != NULL) && (strcmp(nameAsCStr, scan->name) == 0)) { + for (uint32_t i = 0; i < valueCount_; ++i) { + const char *valueName = valueNames_ + nameOffsets_[i]; + if (strcmp(nameAsCStr, valueName) == 0) { if (outValue) { - *outValue = scan->number; + *outValue = values_[i]; } return YES; } @@ -773,34 +745,28 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { return NO; } -- (void)dealloc { - [name_ release]; - [super dealloc]; -} - - (NSString *)textFormatNameForValue:(int32_t)number { + if (nameOffsets_ == NULL) [self calcValueNameOffsets]; + // Find the EnumValue descriptor and its index. - GPBMessageEnumValueDescription *valueDescriptor = NULL; - NSUInteger valueDescriptorIndex; - for (valueDescriptorIndex = 0; valueDescriptorIndex < valueDescriptionsCount_; + BOOL foundIt = NO; + uint32_t valueDescriptorIndex; + for (valueDescriptorIndex = 0; valueDescriptorIndex < valueCount_; ++valueDescriptorIndex) { - GPBMessageEnumValueDescription *scan = - &valueDescriptions_[valueDescriptorIndex]; - if (scan->number == number) { - valueDescriptor = scan; + if (values_[valueDescriptorIndex] == number) { + foundIt = YES; break; } } - // If we didn't find it, or names were disable at proto compile time, nothing - // we can do. - if (!valueDescriptor || !valueDescriptor->name) { + if (!foundIt) { return nil; } NSString *result = nil; // Naming adds an underscore between enum name and value name, skip that also. - NSString *shortName = @(valueDescriptor->name); + const char *valueName = valueNames_ + nameOffsets_[valueDescriptorIndex]; + NSString *shortName = @(valueName); // See if it is in the map of special format handling. if (extraTextFormatInfo_) { diff --git a/objectivec/GPBDescriptor_PackagePrivate.h b/objectivec/GPBDescriptor_PackagePrivate.h index 7987d9287..e3d0a80f0 100644 --- a/objectivec/GPBDescriptor_PackagePrivate.h +++ b/objectivec/GPBDescriptor_PackagePrivate.h @@ -36,7 +36,7 @@ #import "GPBWireFormat.h" // Describes attributes of the field. -typedef NS_OPTIONS(uint32_t, GPBFieldFlags) { +typedef NS_OPTIONS(uint16_t, GPBFieldFlags) { // These map to standard protobuf concepts. GPBFieldRequired = 1 << 0, GPBFieldRepeated = 1 << 1, @@ -44,6 +44,12 @@ typedef NS_OPTIONS(uint32_t, GPBFieldFlags) { GPBFieldOptional = 1 << 3, GPBFieldHasDefaultValue = 1 << 4, + // Indicates the field needs custom handling for the TextFormat name, if not + // set, the name can be derived from the ObjC name. + GPBFieldTextFormatNameCustom = 1 << 6, + // Indicates the field has an enum descriptor. + GPBFieldHasEnumDescriptor = 1 << 7, + // These are not standard protobuf concepts, they are specific to the // Objective C runtime. @@ -62,35 +68,16 @@ typedef NS_OPTIONS(uint32_t, GPBFieldFlags) { GPBFieldMapKeySFixed64 = 10 << 8, GPBFieldMapKeyBool = 11 << 8, GPBFieldMapKeyString = 12 << 8, - - // Indicates the field needs custom handling for the TextFormat name, if not - // set, the name can be derived from the ObjC name. - GPBFieldTextFormatNameCustom = 1 << 16, - // Indicates the field has an enum descriptor. - GPBFieldHasEnumDescriptor = 1 << 17, }; +// NOTE: The structures defined here have their members ordered to minimize +// their size. This directly impacts the size of apps since these exist per +// field/extension. + // Describes a single field in a protobuf as it is represented as an ivar. typedef struct GPBMessageFieldDescription { // Name of ivar. const char *name; - // The field number for the ivar. - uint32_t number; - // The index (in bits) into _has_storage_. - // > 0: the bit to use for a value being set. - // = 0: no storage used. - // < 0: in a oneOf, use a full int32 to record the field active. - int32_t hasIndex; - // Field flags. Use accessor functions below. - GPBFieldFlags flags; - // Data type of the ivar. - GPBDataType dataType; - // Offset of the variable into it's structure struct. - size_t offset; - // FieldOptions protobuf, serialized as string. - const char *fieldOptions; - - GPBGenericValue defaultValue; // Default value for the ivar. union { const char *className; // Name for message class. // For enums only: If EnumDescriptors are compiled in, it will be that, @@ -98,31 +85,32 @@ typedef struct GPBMessageFieldDescription { GPBEnumDescriptorFunc enumDescFunc; GPBEnumValidationFunc enumVerifier; } dataTypeSpecific; + // The field number for the ivar. + uint32_t number; + // The index (in bits) into _has_storage_. + // >= 0: the bit to use for a value being set. + // = GPBNoHasBit(INT32_MAX): no storage used. + // < 0: in a oneOf, use a full int32 to record the field active. + int32_t hasIndex; + // Offset of the variable into it's structure struct. + uint32_t offset; + // Field flags. Use accessor functions below. + GPBFieldFlags flags; + // Data type of the ivar. + GPBDataType dataType; } GPBMessageFieldDescription; -// Describes a oneof. -typedef struct GPBMessageOneofDescription { - // Name of this enum oneof. - const char *name; - // The index of this oneof in the has_storage. - int32_t index; -} GPBMessageOneofDescription; +// Fields in messages defined in a 'proto2' syntax file can provide a default +// value. This struct provides the default along with the field info. +typedef struct GPBMessageFieldDescriptionWithDefault { + // Default value for the ivar. + GPBGenericValue defaultValue; -// Describes an enum type defined in a .proto file. -typedef struct GPBMessageEnumDescription { - GPBEnumDescriptorFunc enumDescriptorFunc; -} GPBMessageEnumDescription; - -// Describes an individual enum constant of a particular type. -typedef struct GPBMessageEnumValueDescription { - // Name of this enum constant. - const char *name; - // Numeric value of this enum constant. - int32_t number; -} GPBMessageEnumValueDescription; + GPBMessageFieldDescription core; +} GPBMessageFieldDescriptionWithDefault; // Describes attributes of the extension. -typedef NS_OPTIONS(uint32_t, GPBExtensionOptions) { +typedef NS_OPTIONS(uint8_t, GPBExtensionOptions) { // These map to standard protobuf concepts. GPBExtensionRepeated = 1 << 0, GPBExtensionPacked = 1 << 1, @@ -131,65 +119,53 @@ typedef NS_OPTIONS(uint32_t, GPBExtensionOptions) { // An extension typedef struct GPBExtensionDescription { - const char *singletonName; - GPBDataType dataType; - const char *extendedClass; - int32_t fieldNumber; GPBGenericValue defaultValue; + const char *singletonName; + const char *extendedClass; const char *messageOrGroupClassName; - GPBExtensionOptions options; GPBEnumDescriptorFunc enumDescriptorFunc; + int32_t fieldNumber; + GPBDataType dataType; + GPBExtensionOptions options; } GPBExtensionDescription; +typedef NS_OPTIONS(uint32_t, GPBDescriptorInitializationFlags) { + GPBDescriptorInitializationFlag_FieldsWithDefault = 1 << 0, + GPBDescriptorInitializationFlag_WireFormat = 1 << 1, +}; + @interface GPBDescriptor () { @package NSArray *fields_; NSArray *oneofs_; - size_t storageSize_; + uint32_t storageSize_; } -// fieldDescriptions, enumDescriptions, rangeDescriptions, and -// extraTextFormatInfo have to be long lived, they are held as raw pointers. +// fieldDescriptions have to be long lived, they are held as raw pointers. + (instancetype) allocDescriptorForClass:(Class)messageClass rootClass:(Class)rootClass file:(GPBFileDescriptor *)file - fields:(GPBMessageFieldDescription *)fieldDescriptions - fieldCount:(NSUInteger)fieldCount - oneofs:(GPBMessageOneofDescription *)oneofDescriptions - oneofCount:(NSUInteger)oneofCount - enums:(GPBMessageEnumDescription *)enumDescriptions - enumCount:(NSUInteger)enumCount - ranges:(const GPBExtensionRange *)ranges - rangeCount:(NSUInteger)rangeCount - storageSize:(size_t)storageSize - wireFormat:(BOOL)wireFormat; -+ (instancetype) - allocDescriptorForClass:(Class)messageClass - rootClass:(Class)rootClass - file:(GPBFileDescriptor *)file - fields:(GPBMessageFieldDescription *)fieldDescriptions - fieldCount:(NSUInteger)fieldCount - oneofs:(GPBMessageOneofDescription *)oneofDescriptions - oneofCount:(NSUInteger)oneofCount - enums:(GPBMessageEnumDescription *)enumDescriptions - enumCount:(NSUInteger)enumCount - ranges:(const GPBExtensionRange *)ranges - rangeCount:(NSUInteger)rangeCount - storageSize:(size_t)storageSize - wireFormat:(BOOL)wireFormat - extraTextFormatInfo:(const char *)extraTextFormatInfo; + fields:(void *)fieldDescriptions + fieldCount:(uint32_t)fieldCount + storageSize:(uint32_t)storageSize + flags:(GPBDescriptorInitializationFlags)flags; - (instancetype)initWithClass:(Class)messageClass file:(GPBFileDescriptor *)file fields:(NSArray *)fields - oneofs:(NSArray *)oneofs - enums:(NSArray *)enums - extensionRanges:(const GPBExtensionRange *)ranges - extensionRangesCount:(NSUInteger)rangeCount - storageSize:(size_t)storage + storageSize:(uint32_t)storage wireFormat:(BOOL)wireFormat; +// Called right after init to provide extra information to avoid init having +// an explosion of args. These pointers are recorded, so they are expected +// to live for the lifetime of the app. +- (void)setupOneofs:(const char **)oneofNames + count:(uint32_t)count + firstHasIndex:(int32_t)firstHasIndex; +- (void)setupExtraTextInfo:(const char *)extraTextFormatInfo; +- (void)setupExtensionRanges:(const GPBExtensionRange *)ranges count:(int32_t)count; + @end @interface GPBFileDescriptor () @@ -199,14 +175,12 @@ typedef struct GPBExtensionDescription { @interface GPBOneofDescriptor () { @package - GPBMessageOneofDescription *oneofDescription_; + const char *name_; NSArray *fields_; - SEL caseSel_; } -- (instancetype)initWithOneofDescription: - (GPBMessageOneofDescription *)oneofDescription - fields:(NSArray *)fields; +// name must be long lived. +- (instancetype)initWithName:(const char *)name fields:(NSArray *)fields; @end @interface GPBFieldDescriptor () { @@ -222,30 +196,32 @@ typedef struct GPBExtensionDescription { // Single initializer // description has to be long lived, it is held as a raw pointer. -- (instancetype)initWithFieldDescription: - (GPBMessageFieldDescription *)description - rootClass:(Class)rootClass +- (instancetype)initWithFieldDescription:(void *)description + includesDefault:(BOOL)includesDefault syntax:(GPBFileSyntax)syntax; @end @interface GPBEnumDescriptor () -// valueDescriptions and extraTextFormatInfo have to be long lived, they are +// valueNames, values and extraTextFormatInfo have to be long lived, they are // held as raw pointers. + (instancetype) allocDescriptorForName:(NSString *)name - values:(GPBMessageEnumValueDescription *)valueDescriptions - valueCount:(NSUInteger)valueCount + valueNames:(const char *)valueNames + values:(const int32_t *)values + count:(uint32_t)valueCount enumVerifier:(GPBEnumValidationFunc)enumVerifier; + (instancetype) allocDescriptorForName:(NSString *)name - values:(GPBMessageEnumValueDescription *)valueDescriptions - valueCount:(NSUInteger)valueCount + valueNames:(const char *)valueNames + values:(const int32_t *)values + count:(uint32_t)valueCount enumVerifier:(GPBEnumValidationFunc)enumVerifier extraTextFormatInfo:(const char *)extraTextFormatInfo; - (instancetype)initWithName:(NSString *)name - values:(GPBMessageEnumValueDescription *)valueDescriptions - valueCount:(NSUInteger)valueCount + valueNames:(const char *)valueNames + values:(const int32_t *)values + count:(uint32_t)valueCount enumVerifier:(GPBEnumValidationFunc)enumVerifier; @end @@ -314,5 +290,24 @@ GPB_INLINE BOOL GPBExtensionIsWireFormat(GPBExtensionDescription *description) { return (description->options & GPBExtensionSetWireFormat) != 0; } +// Helper for compile time assets. +#ifndef _GPBCompileAssert + #if __has_feature(c_static_assert) || __has_extension(c_static_assert) + #define _GPBCompileAssert(test, msg) _Static_assert((test), #msg) + #else + // Pre-Xcode 7 support. + #define _GPBCompileAssertSymbolInner(line, msg) _GPBCompileAssert ## line ## __ ## msg + #define _GPBCompileAssertSymbol(line, msg) _GPBCompileAssertSymbolInner(line, msg) + #define _GPBCompileAssert(test, msg) \ + typedef char _GPBCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ] + #endif // __has_feature(c_static_assert) || __has_extension(c_static_assert) +#endif // _GPBCompileAssert + +// Sanity check that there isn't padding between the field description +// structures with and without a default. +_GPBCompileAssert(sizeof(GPBMessageFieldDescriptionWithDefault) == + (sizeof(GPBGenericValue) + + sizeof(GPBMessageFieldDescription)), + DescriptionsWithDefault_different_size_than_expected); CF_EXTERN_C_END diff --git a/objectivec/GPBMessage.m b/objectivec/GPBMessage.m index d5e8d37b7..8134e2596 100644 --- a/objectivec/GPBMessage.m +++ b/objectivec/GPBMessage.m @@ -54,18 +54,6 @@ NSString *const GPBExceptionMessageKey = static NSString *const kGPBDataCoderKey = @"GPBData"; -#ifndef _GPBCompileAssert - #if __has_feature(c_static_assert) || __has_extension(c_static_assert) - #define _GPBCompileAssert(test, msg) _Static_assert((test), #msg) - #else - // Pre-Xcode 7 support. - #define _GPBCompileAssertSymbolInner(line, msg) _GPBCompileAssert ## line ## __ ## msg - #define _GPBCompileAssertSymbol(line, msg) _GPBCompileAssertSymbolInner(line, msg) - #define _GPBCompileAssert(test, msg) \ - typedef char _GPBCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ] - #endif // __has_feature(c_static_assert) || __has_extension(c_static_assert) -#endif // _GPBCompileAssert - // // PLEASE REMEMBER: // @@ -568,6 +556,7 @@ static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) { id array = GPBGetObjectIvarWithFieldNoAutocreate(self, field); if (!array) { // Check again after getting the lock. + GPBPrepareReadOnlySemaphore(self); dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER); array = GPBGetObjectIvarWithFieldNoAutocreate(self, field); if (!array) { @@ -598,6 +587,7 @@ static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) { id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); if (!dict) { // Check again after getting the lock. + GPBPrepareReadOnlySemaphore(self); dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER); dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); if (!dict) { @@ -789,14 +779,8 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { file:fileDescriptor fields:NULL fieldCount:0 - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 storageSize:0 - wireFormat:NO]; + flags:0]; } return descriptor; } @@ -809,8 +793,6 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { if ((self = [super init])) { messageStorage_ = (GPBMessage_StoragePtr)( ((uint8_t *)self) + class_getInstanceSize([self class])); - - readOnlySemaphore_ = dispatch_semaphore_create(1); } return self; @@ -886,7 +868,9 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { - (void)dealloc { [self internalClear:NO]; NSCAssert(!autocreator_, @"Autocreator was not cleared before dealloc."); - dispatch_release(readOnlySemaphore_); + if (readOnlySemaphore_) { + dispatch_release(readOnlySemaphore_); + } [super dealloc]; } @@ -1724,6 +1708,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { } // Check for an autocreated value. + GPBPrepareReadOnlySemaphore(self); dispatch_semaphore_wait(readOnlySemaphore_, DISPATCH_TIME_FOREVER); value = [autocreatedExtensionMap_ objectForKey:extension]; if (!value) { @@ -1935,7 +1920,6 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { } } @catch (NSException *exception) { - [message release]; message = nil; if (errorPtr) { *errorPtr = MessageErrorWithReason(GPBMessageErrorCodeMalformedData, @@ -1944,7 +1928,6 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { } #ifdef DEBUG if (message && !message.initialized) { - [message release]; message = nil; if (errorPtr) { *errorPtr = MessageError(GPBMessageErrorCodeMissingRequiredField, nil); @@ -2620,9 +2603,13 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( size_t fieldOffset = field->description_->offset; switch (fieldDataType) { case GPBDataTypeBool: { - BOOL *selfValPtr = (BOOL *)&selfStorage[fieldOffset]; - BOOL *otherValPtr = (BOOL *)&otherStorage[fieldOffset]; - if (*selfValPtr != *otherValPtr) { + // Bools are stored in has_bits to avoid needing explicit space in + // the storage structure. + // (the field number passed to the HasIvar helper doesn't really + // matter since the offset is never negative) + BOOL selfValue = GPBGetHasIvar(self, (int32_t)(fieldOffset), 0); + BOOL otherValue = GPBGetHasIvar(other, (int32_t)(fieldOffset), 0); + if (selfValue != otherValue) { return NO; } break; @@ -2731,8 +2718,12 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( size_t fieldOffset = field->description_->offset; switch (fieldDataType) { case GPBDataTypeBool: { - BOOL *valPtr = (BOOL *)&storage[fieldOffset]; - result = prime * result + *valPtr; + // Bools are stored in has_bits to avoid needing explicit space in + // the storage structure. + // (the field number passed to the HasIvar helper doesn't really + // matter since the offset is never negative) + BOOL value = GPBGetHasIvar(self, (int32_t)(fieldOffset), 0); + result = prime * result + value; break; } case GPBDataTypeSFixed32: @@ -3096,7 +3087,7 @@ static void ResolveIvarSet(GPBFieldDescriptor *field, } else { GPBOneofDescriptor *oneof = field->containingOneof_; if (oneof && (sel == oneof->caseSel_)) { - int32_t index = oneof->oneofDescription_->index; + int32_t index = GPBFieldHasIndex(field); result.impToAdd = imp_implementationWithBlock(^(id obj) { return GPBGetHasOneof(obj, index); }); diff --git a/objectivec/GPBMessage_PackagePrivate.h b/objectivec/GPBMessage_PackagePrivate.h index b7e24fc93..478db2cf0 100644 --- a/objectivec/GPBMessage_PackagePrivate.h +++ b/objectivec/GPBMessage_PackagePrivate.h @@ -67,6 +67,10 @@ typedef struct GPBMessage_Storage *GPBMessage_StoragePtr; // priority inversion: // http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/ // https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html + // Use of readOnlySemaphore_ must be prefaced by a call to + // GPBPrepareReadOnlySemaphore to ensure it has been created. This allows + // readOnlySemaphore_ to be only created when actually needed. + dispatch_once_t readOnlySemaphoreCreationOnce_; dispatch_semaphore_t readOnlySemaphore_; } @@ -103,6 +107,14 @@ typedef struct GPBMessage_Storage *GPBMessage_StoragePtr; CF_EXTERN_C_BEGIN + +// Call this before using the readOnlySemaphore_. This ensures it is created only once. +NS_INLINE void GPBPrepareReadOnlySemaphore(GPBMessage *self) { + dispatch_once(&self->readOnlySemaphoreCreationOnce_, ^{ + self->readOnlySemaphore_ = dispatch_semaphore_create(1); + }); +} + // Returns a new instance that was automatically created by |autocreator| for // its field |field|. GPBMessage *GPBCreateMessageWithAutocreator(Class msgClass, diff --git a/objectivec/GPBProtocolBuffers.m b/objectivec/GPBProtocolBuffers.m index e9cbfb42f..8512af7e6 100644 --- a/objectivec/GPBProtocolBuffers.m +++ b/objectivec/GPBProtocolBuffers.m @@ -46,8 +46,6 @@ #import "GPBWellKnownTypes.m" #import "GPBWireFormat.m" -#import "google/protobuf/Descriptor.pbobjc.m" - // Duration and Timestamp are #imported into GPBWellKnownTypes.m to the // Objective C categories added will always be linked in with the classes. #import "google/protobuf/Any.pbobjc.m" diff --git a/objectivec/GPBRuntimeTypes.h b/objectivec/GPBRuntimeTypes.h index e91d86a64..0a38b110c 100644 --- a/objectivec/GPBRuntimeTypes.h +++ b/objectivec/GPBRuntimeTypes.h @@ -67,7 +67,7 @@ typedef union { // Do not change the order of this enum (or add things to it) without thinking // about it very carefully. There are several things that depend on the order. -typedef enum { +typedef NS_ENUM(uint8_t, GPBDataType) { GPBDataTypeBool = 0, GPBDataTypeFixed32, GPBDataTypeSFixed32, @@ -86,7 +86,7 @@ typedef enum { GPBDataTypeMessage, GPBDataTypeGroup, GPBDataTypeEnum, -} GPBDataType; +}; enum { // A count of the number of types in GPBDataType. Separated out from the diff --git a/objectivec/GPBUtilities.m b/objectivec/GPBUtilities.m index d4d6471fd..447c749aa 100644 --- a/objectivec/GPBUtilities.m +++ b/objectivec/GPBUtilities.m @@ -145,9 +145,8 @@ void GPBSetHasIvar(GPBMessage *self, int32_t idx, uint32_t fieldNumber, } void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, - uint32_t fieldNumberNotToClear) { - int32_t hasIndex = oneof->oneofDescription_->index; - uint32_t fieldNumberSet = GPBGetHasOneof(self, hasIndex); + int32_t oneofHasIndex, uint32_t fieldNumberNotToClear) { + uint32_t fieldNumberSet = GPBGetHasOneof(self, oneofHasIndex); if ((fieldNumberSet == fieldNumberNotToClear) || (fieldNumberSet == 0)) { // Do nothing/nothing set in the oneof. return; @@ -168,7 +167,7 @@ void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, // Set to nothing stored in the oneof. // (field number doesn't matter since setting to nothing). - GPBSetHasIvar(self, hasIndex, 1, NO); + GPBSetHasIvar(self, oneofHasIndex, 1, NO); } #pragma mark - IVar accessors @@ -200,7 +199,8 @@ void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, //% NAME$S GPBFileSyntax syntax) { //% GPBOneofDescriptor *oneof = field->containingOneof_; //% if (oneof) { -//% GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field)); +//% GPBMessageFieldDescription *fieldDesc = field->description_; +//% GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); //% } //% NSCAssert(self->messageStorage_ != NULL, //% @"%@: All messages should have storage (from init)", @@ -321,7 +321,8 @@ void GPBSetRetainedObjectIvarWithFieldInternal(GPBMessage *self, // oneof. GPBOneofDescriptor *oneof = field->containingOneof_; if (oneof) { - GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field)); + GPBMessageFieldDescription *fieldDesc = field->description_; + GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } // Clear "has" if they are being set to nil. BOOL setHasValue = (value != nil); @@ -411,6 +412,7 @@ id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) { return field.defaultValue.valueMessage; } + GPBPrepareReadOnlySemaphore(self); dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER); GPBMessage *result = GPBGetObjectIvarWithFieldNoAutocreate(self, field); if (!result) { @@ -476,15 +478,15 @@ void GPBSetMessageRawEnumField(GPBMessage *self, GPBFieldDescriptor *field, GPBSetInt32IvarWithFieldInternal(self, field, value, syntax); } -//%PDDM-EXPAND IVAR_POD_ACCESSORS_DEFN(Bool, BOOL) -// This block of code is generated, do not edit it directly. - BOOL GPBGetMessageBoolField(GPBMessage *self, GPBFieldDescriptor *field) { if (GPBGetHasIvarField(self, field)) { - uint8_t *storage = (uint8_t *)self->messageStorage_; - BOOL *typePtr = (BOOL *)&storage[field->description_->offset]; - return *typePtr; + // Bools are stored in the has bits to avoid needing explicit space in the + // storage structure. + // (the field number passed to the HasIvar helper doesn't really matter + // since the offset is never negative) + GPBMessageFieldDescription *fieldDesc = field->description_; + return GPBGetHasIvar(self, (int32_t)(fieldDesc->offset), fieldDesc->number); } else { return field.defaultValue.valueBool; } @@ -503,19 +505,18 @@ void GPBSetBoolIvarWithFieldInternal(GPBMessage *self, GPBFieldDescriptor *field, BOOL value, GPBFileSyntax syntax) { + GPBMessageFieldDescription *fieldDesc = field->description_; GPBOneofDescriptor *oneof = field->containingOneof_; if (oneof) { - GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field)); + GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } - NSCAssert(self->messageStorage_ != NULL, - @"%@: All messages should have storage (from init)", - [self class]); -#if defined(__clang_analyzer__) - if (self->messageStorage_ == NULL) return; -#endif - uint8_t *storage = (uint8_t *)self->messageStorage_; - BOOL *typePtr = (BOOL *)&storage[field->description_->offset]; - *typePtr = value; + + // Bools are stored in the has bits to avoid needing explicit space in the + // storage structure. + // (the field number passed to the HasIvar helper doesn't really matter since + // the offset is never negative) + GPBSetHasIvar(self, (int32_t)(fieldDesc->offset), fieldDesc->number, value); + // proto2: any value counts as having been set; proto3, it // has to be a non zero value. BOOL hasValue = @@ -553,7 +554,8 @@ void GPBSetInt32IvarWithFieldInternal(GPBMessage *self, GPBFileSyntax syntax) { GPBOneofDescriptor *oneof = field->containingOneof_; if (oneof) { - GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field)); + GPBMessageFieldDescription *fieldDesc = field->description_; + GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } NSCAssert(self->messageStorage_ != NULL, @"%@: All messages should have storage (from init)", @@ -601,7 +603,8 @@ void GPBSetUInt32IvarWithFieldInternal(GPBMessage *self, GPBFileSyntax syntax) { GPBOneofDescriptor *oneof = field->containingOneof_; if (oneof) { - GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field)); + GPBMessageFieldDescription *fieldDesc = field->description_; + GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } NSCAssert(self->messageStorage_ != NULL, @"%@: All messages should have storage (from init)", @@ -649,7 +652,8 @@ void GPBSetInt64IvarWithFieldInternal(GPBMessage *self, GPBFileSyntax syntax) { GPBOneofDescriptor *oneof = field->containingOneof_; if (oneof) { - GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field)); + GPBMessageFieldDescription *fieldDesc = field->description_; + GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } NSCAssert(self->messageStorage_ != NULL, @"%@: All messages should have storage (from init)", @@ -697,7 +701,8 @@ void GPBSetUInt64IvarWithFieldInternal(GPBMessage *self, GPBFileSyntax syntax) { GPBOneofDescriptor *oneof = field->containingOneof_; if (oneof) { - GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field)); + GPBMessageFieldDescription *fieldDesc = field->description_; + GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } NSCAssert(self->messageStorage_ != NULL, @"%@: All messages should have storage (from init)", @@ -745,7 +750,8 @@ void GPBSetFloatIvarWithFieldInternal(GPBMessage *self, GPBFileSyntax syntax) { GPBOneofDescriptor *oneof = field->containingOneof_; if (oneof) { - GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field)); + GPBMessageFieldDescription *fieldDesc = field->description_; + GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } NSCAssert(self->messageStorage_ != NULL, @"%@: All messages should have storage (from init)", @@ -793,7 +799,8 @@ void GPBSetDoubleIvarWithFieldInternal(GPBMessage *self, GPBFileSyntax syntax) { GPBOneofDescriptor *oneof = field->containingOneof_; if (oneof) { - GPBMaybeClearOneof(self, oneof, GPBFieldNumber(field)); + GPBMessageFieldDescription *fieldDesc = field->description_; + GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } NSCAssert(self->messageStorage_ != NULL, @"%@: All messages should have storage (from init)", @@ -812,7 +819,7 @@ void GPBSetDoubleIvarWithFieldInternal(GPBMessage *self, GPBBecomeVisibleToAutocreator(self); } -//%PDDM-EXPAND-END (7 expansions) +//%PDDM-EXPAND-END (6 expansions) // Aliases are function calls that are virtually the same. diff --git a/objectivec/GPBUtilities_PackagePrivate.h b/objectivec/GPBUtilities_PackagePrivate.h index cac551f6f..a6b6c84d9 100644 --- a/objectivec/GPBUtilities_PackagePrivate.h +++ b/objectivec/GPBUtilities_PackagePrivate.h @@ -185,7 +185,7 @@ GPB_INLINE void GPBSetHasIvarField(GPBMessage *self, GPBFieldDescriptor *field, } void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, - uint32_t fieldNumberNotToClear); + int32_t oneofHasIndex, uint32_t fieldNumberNotToClear); //%PDDM-DEFINE GPB_IVAR_SET_DECL(NAME, TYPE) //%void GPBSet##NAME##IvarWithFieldInternal(GPBMessage *self, diff --git a/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj b/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj index 78c3f9bac..309342543 100644 --- a/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj +++ b/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 2CFB390415C718CE00CBF84D /* Descriptor.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD3982214BE5B0C0081D629 /* Descriptor.pbobjc.m */; }; 5102DABC1891A073002037B6 /* GPBConcurrencyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5102DABB1891A052002037B6 /* GPBConcurrencyTests.m */; }; 7461B5360F94FB4600A0C422 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 7461B53C0F94FB4E00A0C422 /* GPBCodedInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B48F0F94F99000A0C422 /* GPBCodedInputStream.m */; }; @@ -27,7 +26,6 @@ 8B79657D14992E3F002FFBFC /* GPBRootObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B79657914992E3E002FFBFC /* GPBRootObject.m */; }; 8B8B615D17DF7056002EE618 /* GPBARCUnittestProtos.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B8B615C17DF7056002EE618 /* GPBARCUnittestProtos.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 8B96157414C8C38C00A2AC0B /* GPBDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B96157314C8C38C00A2AC0B /* GPBDescriptor.m */; }; - 8B96157514CA019D00A2AC0B /* Descriptor.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD3982214BE5B0C0081D629 /* Descriptor.pbobjc.m */; }; 8BBEA4A9147C727D00C4ADB7 /* GPBCodedInputStreamTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B69B0F94FDF800A0C422 /* GPBCodedInputStreamTests.m */; }; 8BBEA4AA147C727D00C4ADB7 /* GPBCodedOuputStreamTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B69D0F94FDF800A0C422 /* GPBCodedOuputStreamTests.m */; }; 8BBEA4AC147C727D00C4ADB7 /* GPBMessageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B6A30F94FDF800A0C422 /* GPBMessageTests.m */; }; @@ -140,10 +138,8 @@ 8B4248D51A92826400BC1EC6 /* Timestamp.pbobjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Timestamp.pbobjc.h; path = google/protobuf/Timestamp.pbobjc.h; sourceTree = ""; }; 8B4248D61A92826400BC1EC6 /* Timestamp.pbobjc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Timestamp.pbobjc.m; path = google/protobuf/Timestamp.pbobjc.m; sourceTree = ""; }; 8B4248DB1A92933A00BC1EC6 /* GPBWellKnownTypesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBWellKnownTypesTest.m; sourceTree = ""; }; - 8B42494B1A92A16600BC1EC6 /* descriptor.proto */ = {isa = PBXFileReference; lastKnownFileType = text; name = descriptor.proto; path = ../src/google/protobuf/descriptor.proto; sourceTree = ""; }; 8B42494C1A92A16600BC1EC6 /* duration.proto */ = {isa = PBXFileReference; lastKnownFileType = text; name = duration.proto; path = ../src/google/protobuf/duration.proto; sourceTree = ""; }; 8B42494D1A92A16600BC1EC6 /* timestamp.proto */ = {isa = PBXFileReference; lastKnownFileType = text; name = timestamp.proto; path = ../src/google/protobuf/timestamp.proto; sourceTree = ""; }; - 8B54585814DCC34E003D7338 /* Descriptor.pbobjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Descriptor.pbobjc.h; path = google/protobuf/Descriptor.pbobjc.h; sourceTree = SOURCE_ROOT; }; 8B79657814992E3E002FFBFC /* GPBRootObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GPBRootObject.h; sourceTree = ""; }; 8B79657914992E3E002FFBFC /* GPBRootObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBRootObject.m; sourceTree = ""; }; 8B7E6A7414893DBA00F8884A /* unittest_custom_options.proto */ = {isa = PBXFileReference; lastKnownFileType = text; name = unittest_custom_options.proto; path = ../../src/google/protobuf/unittest_custom_options.proto; sourceTree = ""; }; @@ -162,7 +158,6 @@ 8BCF338814ED799900BC5317 /* GPBProtocolBuffers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GPBProtocolBuffers.m; sourceTree = ""; }; 8BD3981D14BE54220081D629 /* unittest_enormous_descriptor.proto */ = {isa = PBXFileReference; lastKnownFileType = text; name = unittest_enormous_descriptor.proto; path = ../../src/google/protobuf/unittest_enormous_descriptor.proto; sourceTree = ""; }; 8BD3981E14BE59D70081D629 /* GPBUnittestProtos.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBUnittestProtos.m; sourceTree = ""; }; - 8BD3982214BE5B0C0081D629 /* Descriptor.pbobjc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Descriptor.pbobjc.m; path = google/protobuf/Descriptor.pbobjc.m; sourceTree = SOURCE_ROOT; }; 8BEB5AE01498033E0078BF9D /* GPBRuntimeTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBRuntimeTypes.h; sourceTree = ""; }; F401DC2A1A8D444600FCC765 /* GPBArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GPBArray.h; sourceTree = ""; }; F401DC2B1A8D444600FCC765 /* GPBArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBArray.m; sourceTree = ""; }; @@ -300,9 +295,6 @@ F4E675881B21D0000054530B /* Api.pbobjc.h */, F4E675891B21D0000054530B /* Api.pbobjc.m */, F4E675A71B21D05C0054530B /* api.proto */, - 8B54585814DCC34E003D7338 /* Descriptor.pbobjc.h */, - 8BD3982214BE5B0C0081D629 /* Descriptor.pbobjc.m */, - 8B42494B1A92A16600BC1EC6 /* descriptor.proto */, 8B4248D31A92826400BC1EC6 /* Duration.pbobjc.h */, 8B4248D41A92826400BC1EC6 /* Duration.pbobjc.m */, 8B42494C1A92A16600BC1EC6 /* duration.proto */, @@ -634,7 +626,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2CFB390415C718CE00CBF84D /* Descriptor.pbobjc.m in Sources */, 7461B53C0F94FB4E00A0C422 /* GPBCodedInputStream.m in Sources */, F4E6759B1B21D0000054530B /* Empty.pbobjc.m in Sources */, 7461B53D0F94FB4E00A0C422 /* GPBCodedOutputStream.m in Sources */, @@ -696,7 +687,6 @@ 8B79657D14992E3F002FFBFC /* GPBRootObject.m in Sources */, 8BD3981F14BE59D70081D629 /* GPBUnittestProtos.m in Sources */, F4E675B11B21D0A70054530B /* FieldMask.pbobjc.m in Sources */, - 8B96157514CA019D00A2AC0B /* Descriptor.pbobjc.m in Sources */, 8B8B615D17DF7056002EE618 /* GPBARCUnittestProtos.m in Sources */, F4E675AF1B21D0A70054530B /* Api.pbobjc.m in Sources */, ); diff --git a/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj b/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj index 5d96c05f5..b62218193 100644 --- a/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj +++ b/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 2CFB390415C718CE00CBF84D /* Descriptor.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD3982214BE5B0C0081D629 /* Descriptor.pbobjc.m */; }; 5102DABC1891A073002037B6 /* GPBConcurrencyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5102DABB1891A052002037B6 /* GPBConcurrencyTests.m */; }; 7461B5360F94FB4600A0C422 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 7461B53C0F94FB4E00A0C422 /* GPBCodedInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B48F0F94F99000A0C422 /* GPBCodedInputStream.m */; }; @@ -27,7 +26,6 @@ 8B79657D14992E3F002FFBFC /* GPBRootObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B79657914992E3E002FFBFC /* GPBRootObject.m */; }; 8B8B615D17DF7056002EE618 /* GPBARCUnittestProtos.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B8B615C17DF7056002EE618 /* GPBARCUnittestProtos.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 8B96157414C8C38C00A2AC0B /* GPBDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B96157314C8C38C00A2AC0B /* GPBDescriptor.m */; }; - 8B96157514CA019D00A2AC0B /* Descriptor.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD3982214BE5B0C0081D629 /* Descriptor.pbobjc.m */; }; 8B9742331A89D19F00DCE92C /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8B9742321A89D19F00DCE92C /* LaunchScreen.xib */; }; 8B9742431A8AAA7800DCE92C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B9742421A8AAA7800DCE92C /* CoreGraphics.framework */; }; 8B9A5EA61831993600A9D33B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; @@ -155,7 +153,6 @@ 8B4248E21A929C8900BC1EC6 /* GPBWellKnownTypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBWellKnownTypes.m; sourceTree = ""; }; 8B4248E51A929C9900BC1EC6 /* GPBWellKnownTypesTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBWellKnownTypesTest.m; sourceTree = ""; }; 8B4249481A92A02300BC1EC6 /* timestamp.proto */ = {isa = PBXFileReference; lastKnownFileType = text; name = timestamp.proto; path = ../src/google/protobuf/timestamp.proto; sourceTree = ""; }; - 8B4249491A92A0BA00BC1EC6 /* descriptor.proto */ = {isa = PBXFileReference; lastKnownFileType = text; name = descriptor.proto; path = ../src/google/protobuf/descriptor.proto; sourceTree = ""; }; 8B42494A1A92A0BA00BC1EC6 /* duration.proto */ = {isa = PBXFileReference; lastKnownFileType = text; name = duration.proto; path = ../src/google/protobuf/duration.proto; sourceTree = ""; }; 8B79657814992E3E002FFBFC /* GPBRootObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GPBRootObject.h; sourceTree = ""; }; 8B79657914992E3E002FFBFC /* GPBRootObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBRootObject.m; sourceTree = ""; }; @@ -183,7 +180,6 @@ 8BCF338814ED799900BC5317 /* GPBProtocolBuffers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GPBProtocolBuffers.m; sourceTree = ""; }; 8BD3981D14BE54220081D629 /* unittest_enormous_descriptor.proto */ = {isa = PBXFileReference; lastKnownFileType = text; name = unittest_enormous_descriptor.proto; path = ../../src/google/protobuf/unittest_enormous_descriptor.proto; sourceTree = ""; }; 8BD3981E14BE59D70081D629 /* GPBUnittestProtos.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBUnittestProtos.m; sourceTree = ""; }; - 8BD3982214BE5B0C0081D629 /* Descriptor.pbobjc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Descriptor.pbobjc.m; path = google/protobuf/Descriptor.pbobjc.m; sourceTree = SOURCE_ROOT; }; 8BEB5AE01498033E0078BF9D /* GPBRuntimeTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBRuntimeTypes.h; sourceTree = ""; }; F401DC341A8E5C6F00FCC765 /* GPBArrayTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBArrayTests.m; sourceTree = ""; }; F41C175C1833D3310064ED4D /* GPBPerfTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBPerfTests.m; sourceTree = ""; }; @@ -224,7 +220,6 @@ F4E675B71B21D1440054530B /* Any.pbobjc.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Any.pbobjc.m; path = google/protobuf/Any.pbobjc.m; sourceTree = ""; }; F4E675B81B21D1440054530B /* Api.pbobjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Api.pbobjc.h; path = google/protobuf/Api.pbobjc.h; sourceTree = ""; }; F4E675B91B21D1440054530B /* Api.pbobjc.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Api.pbobjc.m; path = google/protobuf/Api.pbobjc.m; sourceTree = ""; }; - F4E675BA1B21D1440054530B /* Descriptor.pbobjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Descriptor.pbobjc.h; path = google/protobuf/Descriptor.pbobjc.h; sourceTree = ""; }; F4E675BB1B21D1440054530B /* Empty.pbobjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Empty.pbobjc.h; path = google/protobuf/Empty.pbobjc.h; sourceTree = ""; }; F4E675BC1B21D1440054530B /* Empty.pbobjc.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Empty.pbobjc.m; path = google/protobuf/Empty.pbobjc.m; sourceTree = ""; }; F4E675BD1B21D1440054530B /* FieldMask.pbobjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FieldMask.pbobjc.h; path = google/protobuf/FieldMask.pbobjc.h; sourceTree = ""; }; @@ -335,9 +330,6 @@ F4E675B81B21D1440054530B /* Api.pbobjc.h */, F4E675B91B21D1440054530B /* Api.pbobjc.m */, F4E675D91B21D1DE0054530B /* api.proto */, - F4E675BA1B21D1440054530B /* Descriptor.pbobjc.h */, - 8BD3982214BE5B0C0081D629 /* Descriptor.pbobjc.m */, - 8B4249491A92A0BA00BC1EC6 /* descriptor.proto */, 8B4248DD1A929C7D00BC1EC6 /* Duration.pbobjc.h */, 8B4248DE1A929C7D00BC1EC6 /* Duration.pbobjc.m */, 8B42494A1A92A0BA00BC1EC6 /* duration.proto */, @@ -722,7 +714,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2CFB390415C718CE00CBF84D /* Descriptor.pbobjc.m in Sources */, 7461B53C0F94FB4E00A0C422 /* GPBCodedInputStream.m in Sources */, F4E675D21B21D1620054530B /* Empty.pbobjc.m in Sources */, F4487C731A9F906200531423 /* GPBArray.m in Sources */, @@ -792,7 +783,6 @@ 8B79657D14992E3F002FFBFC /* GPBRootObject.m in Sources */, 8BD3981F14BE59D70081D629 /* GPBUnittestProtos.m in Sources */, F4E675CB1B21D1610054530B /* FieldMask.pbobjc.m in Sources */, - 8B96157514CA019D00A2AC0B /* Descriptor.pbobjc.m in Sources */, 8B8B615D17DF7056002EE618 /* GPBARCUnittestProtos.m in Sources */, F4E675C91B21D1610054530B /* Api.pbobjc.m in Sources */, ); diff --git a/objectivec/Tests/GPBARCUnittestProtos.m b/objectivec/Tests/GPBARCUnittestProtos.m index d0408869b..28d2396c2 100644 --- a/objectivec/Tests/GPBARCUnittestProtos.m +++ b/objectivec/Tests/GPBARCUnittestProtos.m @@ -34,6 +34,11 @@ // Makes sure all the generated headers compile with ARC on. +// The unittest_custom_options.proto extends the messages in descriptor.proto +// so we build it in to test extending in general. The library doesn't provide +// a descriptor as it doesn't use the classes/enums. +#import "google/protobuf/Descriptor.pbobjc.h" + #import "google/protobuf/Unittest.pbobjc.h" #import "google/protobuf/UnittestCustomOptions.pbobjc.h" #import "google/protobuf/UnittestCycle.pbobjc.h" diff --git a/objectivec/Tests/GPBCodedInputStreamTests.m b/objectivec/Tests/GPBCodedInputStreamTests.m index b0e39d2cf..cc4021569 100644 --- a/objectivec/Tests/GPBCodedInputStreamTests.m +++ b/objectivec/Tests/GPBCodedInputStreamTests.m @@ -283,16 +283,53 @@ [output writeRawData:[NSData dataWithBytes:bytes length:sizeof(bytes)]]; [output flush]; - NSData* data = + NSData *data = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; GPBCodedInputStream* input = [GPBCodedInputStream streamWithData:data]; + NSError *error = nil; TestAllTypes* message = [TestAllTypes parseFromCodedInputStream:input extensionRegistry:nil - error:NULL]; - XCTAssertNotNil(message); - // Make sure we can read string properties twice without crashing. - XCTAssertEqual([message.defaultString length], (NSUInteger)0); - XCTAssertEqualObjects(@"", message.defaultString); + error:&error]; + XCTAssertNotNil(error); + XCTAssertNil(message); +} + +- (void)testBOMWithinStrings { + // We've seen servers that end up with BOMs within strings (not always at the + // start, and sometimes in multiple places), make sure they always parse + // correctly. (Again, this is inpart incase a custom string class is ever + // used again.) + const char* strs[] = { + "\xEF\xBB\xBF String with BOM", + "String with \xEF\xBB\xBF in middle", + "String with end bom \xEF\xBB\xBF", + "\xEF\xBB\xBF\xe2\x99\xa1", // BOM White Heart + "\xEF\xBB\xBF\xEF\xBB\xBF String with Two BOM", + }; + for (size_t i = 0; i < GPBARRAYSIZE(strs); ++i) { + NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory]; + GPBCodedOutputStream* output = + [GPBCodedOutputStream streamWithOutputStream:rawOutput]; + + int32_t tag = GPBWireFormatMakeTag(TestAllTypes_FieldNumber_DefaultString, + GPBWireFormatLengthDelimited); + [output writeRawVarint32:tag]; + size_t length = strlen(strs[i]); + [output writeRawVarint32:(int32_t)length]; + [output writeRawData:[NSData dataWithBytes:strs[i] length:length]]; + [output flush]; + + NSData* data = + [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; + GPBCodedInputStream* input = [GPBCodedInputStream streamWithData:data]; + TestAllTypes* message = [TestAllTypes parseFromCodedInputStream:input + extensionRegistry:nil + error:NULL]; + XCTAssertNotNil(message, @"Loop %zd", i); + // Ensure the string is there. NSString can consume the BOM in some + // cases, so don't actually check the string for exact equality. + XCTAssertTrue(message.defaultString.length > 0, @"Loop %zd", i); + } } @end diff --git a/objectivec/Tests/GPBDescriptorTests.m b/objectivec/Tests/GPBDescriptorTests.m index ccdbb6453..a1923c9cc 100644 --- a/objectivec/Tests/GPBDescriptorTests.m +++ b/objectivec/Tests/GPBDescriptorTests.m @@ -153,19 +153,6 @@ XCTAssertFalse([fieldDescriptor isValidEnumValue:-2]); } -- (void)testEnumDescriptorLookup { - GPBDescriptor *descriptor = [TestAllTypes descriptor]; - GPBEnumDescriptor *enumDescriptor = - [descriptor enumWithName:@"TestAllTypes_NestedEnum"]; - XCTAssertNotNil(enumDescriptor); - - // Descriptor cannot find foreign or imported enums. - enumDescriptor = [descriptor enumWithName:@"ForeignEnumEnum"]; - XCTAssertNil(enumDescriptor); - enumDescriptor = [descriptor enumWithName:@"ImportEnumEnum"]; - XCTAssertNil(enumDescriptor); -} - - (void)testOneofDescriptor { GPBDescriptor *descriptor = [TestOneof2 descriptor]; diff --git a/objectivec/Tests/GPBMessageTests.m b/objectivec/Tests/GPBMessageTests.m index 43546156c..89d1fce2c 100644 --- a/objectivec/Tests/GPBMessageTests.m +++ b/objectivec/Tests/GPBMessageTests.m @@ -1947,4 +1947,76 @@ EnumTestMsg_MyEnum_NegTwo); } +- (void)testOneBasedEnumHolder { + // Test case for https://github.com/google/protobuf/issues/1453 + // Message with no explicit defaults, but a non zero default for an enum. + MessageWithOneBasedEnum *enumMsg = [MessageWithOneBasedEnum message]; + XCTAssertEqual(enumMsg.enumField, MessageWithOneBasedEnum_OneBasedEnum_One); +} + +- (void)testBoolOffsetUsage { + // Bools use storage within has_bits; this test ensures that this is honored + // in all places where things should crash or fail based on reading out of + // field storage instead. + BoolOnlyMessage *msg1 = [BoolOnlyMessage message]; + BoolOnlyMessage *msg2 = [BoolOnlyMessage message]; + + msg1.boolField1 = YES; + msg2.boolField1 = YES; + msg1.boolField3 = YES; + msg2.boolField3 = YES; + msg1.boolField5 = YES; + msg2.boolField5 = YES; + msg1.boolField7 = YES; + msg2.boolField7 = YES; + msg1.boolField9 = YES; + msg2.boolField9 = YES; + msg1.boolField11 = YES; + msg2.boolField11 = YES; + msg1.boolField13 = YES; + msg2.boolField13 = YES; + msg1.boolField15 = YES; + msg2.boolField15 = YES; + msg1.boolField17 = YES; + msg2.boolField17 = YES; + msg1.boolField19 = YES; + msg2.boolField19 = YES; + msg1.boolField21 = YES; + msg2.boolField21 = YES; + msg1.boolField23 = YES; + msg2.boolField23 = YES; + msg1.boolField25 = YES; + msg2.boolField25 = YES; + msg1.boolField27 = YES; + msg2.boolField27 = YES; + msg1.boolField29 = YES; + msg2.boolField29 = YES; + msg1.boolField31 = YES; + msg2.boolField31 = YES; + + msg1.boolField32 = YES; + msg2.boolField32 = YES; + + XCTAssertTrue(msg1 != msg2); // Different pointers. + XCTAssertEqual([msg1 hash], [msg2 hash]); + XCTAssertEqualObjects(msg1, msg2); + + BoolOnlyMessage *msg1Prime = [[msg1 copy] autorelease]; + XCTAssertTrue(msg1Prime != msg1); // Different pointers. + XCTAssertEqual([msg1 hash], [msg1Prime hash]); + XCTAssertEqualObjects(msg1, msg1Prime); + + // Field set in one, but not the other means they don't match (even if + // set to default value). + msg1Prime.boolField2 = NO; + XCTAssertNotEqualObjects(msg1Prime, msg1); + // And when set to different values. + msg1.boolField2 = YES; + XCTAssertNotEqualObjects(msg1Prime, msg1); + // And then they match again. + msg1.boolField2 = NO; + XCTAssertEqualObjects(msg1Prime, msg1); + XCTAssertEqual([msg1 hash], [msg1Prime hash]); +} + @end diff --git a/objectivec/Tests/GPBUnittestProtos.m b/objectivec/Tests/GPBUnittestProtos.m index 50c4dfa98..d19beee92 100644 --- a/objectivec/Tests/GPBUnittestProtos.m +++ b/objectivec/Tests/GPBUnittestProtos.m @@ -31,6 +31,11 @@ // Collects all the compiled protos into one file and compiles them to make sure // the compiler is generating valid code. +// The unittest_custom_options.proto extends the messages in descriptor.proto +// so we build it in to test extending in general. The library doesn't provide +// a descriptor as it doesn't use the classes/enums. +#import "google/protobuf/Descriptor.pbobjc.m" + #import "google/protobuf/MapProto2Unittest.pbobjc.m" #import "google/protobuf/MapUnittest.pbobjc.m" #import "google/protobuf/Unittest.pbobjc.m" diff --git a/objectivec/Tests/unittest_objc.proto b/objectivec/Tests/unittest_objc.proto index 9483cb1d7..f6ab6a24c 100644 --- a/objectivec/Tests/unittest_objc.proto +++ b/objectivec/Tests/unittest_objc.proto @@ -401,3 +401,49 @@ message EnumTestMsg { repeated MyEnum mumble = 4; } + +// Test case for https://github.com/google/protobuf/issues/1453 +// Message with no explicit defaults, but a non zero default for an enum. +message MessageWithOneBasedEnum { + enum OneBasedEnum { + ONE = 1; + TWO = 2; + } + optional OneBasedEnum enum_field = 1; +} + +// Message with all bools for testing things related to bool storage. +message BoolOnlyMessage { + optional bool bool_field_1 = 1; + optional bool bool_field_2 = 2; + optional bool bool_field_3 = 3; + optional bool bool_field_4 = 4; + optional bool bool_field_5 = 5; + optional bool bool_field_6 = 6; + optional bool bool_field_7 = 7; + optional bool bool_field_8 = 8; + optional bool bool_field_9 = 9; + optional bool bool_field_10 = 10; + optional bool bool_field_11 = 11; + optional bool bool_field_12 = 12; + optional bool bool_field_13 = 13; + optional bool bool_field_14 = 14; + optional bool bool_field_15 = 15; + optional bool bool_field_16 = 16; + optional bool bool_field_17 = 17; + optional bool bool_field_18 = 18; + optional bool bool_field_19 = 19; + optional bool bool_field_20 = 20; + optional bool bool_field_21 = 21; + optional bool bool_field_22 = 22; + optional bool bool_field_23 = 23; + optional bool bool_field_24 = 24; + optional bool bool_field_25 = 25; + optional bool bool_field_26 = 26; + optional bool bool_field_27 = 27; + optional bool bool_field_28 = 28; + optional bool bool_field_29 = 29; + optional bool bool_field_30 = 30; + optional bool bool_field_31 = 31; + optional bool bool_field_32 = 32; +} diff --git a/objectivec/generate_descriptors_proto.sh b/objectivec/generate_well_known_types.sh similarity index 53% rename from objectivec/generate_descriptors_proto.sh rename to objectivec/generate_well_known_types.sh index 84ba07380..36c346031 100755 --- a/objectivec/generate_descriptors_proto.sh +++ b/objectivec/generate_well_known_types.sh @@ -1,9 +1,9 @@ #!/bin/bash -# Run this script to regenerate descriptor.pbobjc.{h,m} after the protocol -# compiler changes. +# Run this script to regenerate *.pbobjc.{h,m} for the well known types after +# the protocol compiler changes. -# HINT: Flags passed to generate_descriptor_proto.sh will be passed directly +# HINT: Flags passed to generate_well_known_types.sh will be passed directly # to make when building protoc. This is particularly useful for passing # -j4 to run 4 jobs simultaneously. @@ -12,6 +12,13 @@ set -eu readonly ScriptDir=$(dirname "$(echo $0 | sed -e "s,^\([^/]\),$(pwd)/\1,")") readonly ProtoRootDir="${ScriptDir}/.." +# Flag for continuous integration to check that everything is current. +CHECK_ONLY=0 +if [[ $# -ge 1 && ( "$1" == "--check-only" ) ]] ; then + CHECK_ONLY=1 + shift +fi + pushd "${ProtoRootDir}" > /dev/null if test ! -e src/google/protobuf/stubs/common.h; then @@ -37,7 +44,6 @@ make $@ protoc declare -a RUNTIME_PROTO_FILES=( \ google/protobuf/any.proto \ google/protobuf/api.proto \ - google/protobuf/descriptor.proto \ google/protobuf/duration.proto \ google/protobuf/empty.proto \ google/protobuf/field_mask.proto \ @@ -47,4 +53,24 @@ declare -a RUNTIME_PROTO_FILES=( \ google/protobuf/type.proto \ google/protobuf/wrappers.proto) -./protoc --objc_out="${ProtoRootDir}/objectivec" ${RUNTIME_PROTO_FILES[@]} +# Generate to a temp directory to see if they match. +TMP_DIR=$(mktemp -d) +trap "rm -rf ${TMP_DIR}" EXIT +./protoc --objc_out="${TMP_DIR}" ${RUNTIME_PROTO_FILES[@]} +set +e +diff -r "${TMP_DIR}/google" "${ProtoRootDir}/objectivec/google" > /dev/null +if [[ $? -eq 0 ]] ; then + echo "Generated source for WellKnownTypes is current." + exit 0 +fi +set -e + +# If check only mode, error out. +if [[ "${CHECK_ONLY}" == 1 ]] ; then + echo "ERROR: The WKTs need to be regenerated! Run $0" + exit 1 +fi + +# Copy them over. +echo "Copying over updated WellKnownType sources." +cp -r "${TMP_DIR}/google/." "${ProtoRootDir}/objectivec/google/" diff --git a/objectivec/google/protobuf/Any.pbobjc.h b/objectivec/google/protobuf/Any.pbobjc.h index a204ae9af..4002a9891 100644 --- a/objectivec/google/protobuf/Any.pbobjc.h +++ b/objectivec/google/protobuf/Any.pbobjc.h @@ -3,12 +3,15 @@ #import "GPBProtocolBuffers.h" -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 +#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001 #error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. #endif // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CF_EXTERN_C_BEGIN NS_ASSUME_NONNULL_BEGIN @@ -33,8 +36,36 @@ typedef GPB_ENUM(GPBAny_FieldNumber) { GPBAny_FieldNumber_Value = 2, }; -/// `Any` contains an arbitrary serialized message along with a URL -/// that describes the type of the serialized message. +/// `Any` contains an arbitrary serialized protocol buffer message along with a +/// URL that describes the type of the serialized message. +/// +/// Protobuf library provides support to pack/unpack Any values in the form +/// of utility functions or additional generated methods of the Any type. +/// +/// Example 1: Pack and unpack a message in C++. +/// +/// Foo foo = ...; +/// Any any; +/// any.PackFrom(foo); +/// ... +/// if (any.UnpackTo(&foo)) { +/// ... +/// } +/// +/// Example 2: Pack and unpack a message in Java. +/// +/// Foo foo = ...; +/// Any any = Any.pack(foo); +/// ... +/// if (any.is(Foo.class)) { +/// foo = any.unpack(Foo.class); +/// } +/// +/// The pack methods provided by protobuf library will by default use +/// 'type.googleapis.com/full.type.name' as the type URL and the unpack +/// methods only use the fully qualified type name after the last '/' +/// in the type URL, for example "foo.bar.com/x/y.z" will yield type +/// name "y.z". /// /// /// JSON @@ -67,7 +98,7 @@ typedef GPB_ENUM(GPBAny_FieldNumber) { @interface GPBAny : GPBMessage /// A URL/resource name whose content describes the type of the -/// serialized message. +/// serialized protocol buffer message. /// /// For URLs which use the schema `http`, `https`, or no schema, the /// following restrictions and interpretations apply: @@ -75,6 +106,8 @@ typedef GPB_ENUM(GPBAny_FieldNumber) { /// * If no schema is provided, `https` is assumed. /// * The last segment of the URL's path must represent the fully /// qualified name of the type (as in `path/google.protobuf.Duration`). +/// The name should be in a canonical form (e.g., leading "." is +/// not accepted). /// * An HTTP GET on the URL must yield a [google.protobuf.Type][] /// value in binary format, or produce an error. /// * Applications are allowed to cache lookup results based on the @@ -87,7 +120,7 @@ typedef GPB_ENUM(GPBAny_FieldNumber) { /// used with implementation specific semantics. @property(nonatomic, readwrite, copy, null_resettable) NSString *typeURL; -/// Must be valid serialized data of the above specified type. +/// Must be a valid serialized protocol buffer of the above specified type. @property(nonatomic, readwrite, copy, null_resettable) NSData *value; @end @@ -96,4 +129,6 @@ NS_ASSUME_NONNULL_END CF_EXTERN_C_END +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Any.pbobjc.m b/objectivec/google/protobuf/Any.pbobjc.m index b41102a47..6a1bf894f 100644 --- a/objectivec/google/protobuf/Any.pbobjc.m +++ b/objectivec/google/protobuf/Any.pbobjc.m @@ -5,6 +5,9 @@ #import "google/protobuf/Any.pbobjc.h" // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma mark - GPBAnyRoot @implementation GPBAnyRoot @@ -46,47 +49,36 @@ typedef struct GPBAny__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "typeURL", + .dataTypeSpecific.className = NULL, .number = GPBAny_FieldNumber_TypeURL, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBAny__storage_, typeURL), .flags = GPBFieldOptional | GPBFieldTextFormatNameCustom, .dataType = GPBDataTypeString, - .offset = offsetof(GPBAny__storage_, typeURL), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "value", + .dataTypeSpecific.className = NULL, .number = GPBAny_FieldNumber_Value, .hasIndex = 1, + .offset = (uint32_t)offsetof(GPBAny__storage_, value), .flags = GPBFieldOptional, .dataType = GPBDataTypeBytes, - .offset = offsetof(GPBAny__storage_, value), - .defaultValue.valueData = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; -#if GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS - const char *extraTextFormatInfo = NULL; -#else - static const char *extraTextFormatInfo = "\001\001\004\241!!\000"; -#endif // GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS GPBDescriptor *localDescriptor = [GPBDescriptor allocDescriptorForClass:[GPBAny class] rootClass:[GPBAnyRoot class] file:GPBAnyRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBAny__storage_) - wireFormat:NO - extraTextFormatInfo:extraTextFormatInfo]; + flags:0]; +#if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS + static const char *extraTextFormatInfo = + "\001\001\004\241!!\000"; + [localDescriptor setupExtraTextInfo:extraTextFormatInfo]; +#endif // !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -96,4 +88,6 @@ typedef struct GPBAny__storage_ { @end +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Api.pbobjc.h b/objectivec/google/protobuf/Api.pbobjc.h index 271a166f7..d66df6293 100644 --- a/objectivec/google/protobuf/Api.pbobjc.h +++ b/objectivec/google/protobuf/Api.pbobjc.h @@ -3,12 +3,15 @@ #import "GPBProtocolBuffers.h" -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 +#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001 #error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. #endif // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CF_EXTERN_C_BEGIN @class GPBMethod; @@ -254,4 +257,6 @@ NS_ASSUME_NONNULL_END CF_EXTERN_C_END +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Api.pbobjc.m b/objectivec/google/protobuf/Api.pbobjc.m index d964ff412..45a06e600 100644 --- a/objectivec/google/protobuf/Api.pbobjc.m +++ b/objectivec/google/protobuf/Api.pbobjc.m @@ -7,6 +7,9 @@ #import "google/protobuf/Type.pbobjc.h" // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma mark - GPBApiRoot @implementation GPBApiRoot @@ -71,80 +74,66 @@ typedef struct GPBApi__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "name", + .dataTypeSpecific.className = NULL, .number = GPBApi_FieldNumber_Name, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBApi__storage_, name), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBApi__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "methodsArray", + .dataTypeSpecific.className = GPBStringifySymbol(GPBMethod), .number = GPBApi_FieldNumber_MethodsArray, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBApi__storage_, methodsArray), .flags = GPBFieldRepeated, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBApi__storage_, methodsArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBMethod), - .fieldOptions = NULL, }, { .name = "optionsArray", + .dataTypeSpecific.className = GPBStringifySymbol(GPBOption), .number = GPBApi_FieldNumber_OptionsArray, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBApi__storage_, optionsArray), .flags = GPBFieldRepeated, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBApi__storage_, optionsArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBOption), - .fieldOptions = NULL, }, { .name = "version", + .dataTypeSpecific.className = NULL, .number = GPBApi_FieldNumber_Version, - .hasIndex = 3, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GPBApi__storage_, version), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBApi__storage_, version), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "sourceContext", + .dataTypeSpecific.className = GPBStringifySymbol(GPBSourceContext), .number = GPBApi_FieldNumber_SourceContext, - .hasIndex = 4, + .hasIndex = 2, + .offset = (uint32_t)offsetof(GPBApi__storage_, sourceContext), .flags = GPBFieldOptional, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBApi__storage_, sourceContext), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBSourceContext), - .fieldOptions = NULL, }, { .name = "mixinsArray", + .dataTypeSpecific.className = GPBStringifySymbol(GPBMixin), .number = GPBApi_FieldNumber_MixinsArray, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBApi__storage_, mixinsArray), .flags = GPBFieldRepeated, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBApi__storage_, mixinsArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBMixin), - .fieldOptions = NULL, }, { .name = "syntax", + .dataTypeSpecific.enumDescFunc = GPBSyntax_EnumDescriptor, .number = GPBApi_FieldNumber_Syntax, - .hasIndex = 6, + .hasIndex = 3, + .offset = (uint32_t)offsetof(GPBApi__storage_, syntax), .flags = GPBFieldOptional | GPBFieldHasEnumDescriptor, .dataType = GPBDataTypeEnum, - .offset = offsetof(GPBApi__storage_, syntax), - .defaultValue.valueEnum = GPBSyntax_SyntaxProto2, - .dataTypeSpecific.enumDescFunc = GPBSyntax_EnumDescriptor, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -152,15 +141,9 @@ typedef struct GPBApi__storage_ { rootClass:[GPBApiRoot class] file:GPBApiRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBApi__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -195,8 +178,6 @@ void SetGPBApi_Syntax_RawValue(GPBApi *message, int32_t value) { typedef struct GPBMethod__storage_ { uint32_t _has_storage_[1]; - BOOL requestStreaming; - BOOL responseStreaming; GPBSyntax syntax; NSString *name; NSString *requestTypeURL; @@ -212,102 +193,81 @@ typedef struct GPBMethod__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "name", + .dataTypeSpecific.className = NULL, .number = GPBMethod_FieldNumber_Name, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBMethod__storage_, name), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBMethod__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "requestTypeURL", + .dataTypeSpecific.className = NULL, .number = GPBMethod_FieldNumber_RequestTypeURL, .hasIndex = 1, + .offset = (uint32_t)offsetof(GPBMethod__storage_, requestTypeURL), .flags = GPBFieldOptional | GPBFieldTextFormatNameCustom, .dataType = GPBDataTypeString, - .offset = offsetof(GPBMethod__storage_, requestTypeURL), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "requestStreaming", + .dataTypeSpecific.className = NULL, .number = GPBMethod_FieldNumber_RequestStreaming, .hasIndex = 2, + .offset = 3, // Stored in _has_storage_ to save space. .flags = GPBFieldOptional, .dataType = GPBDataTypeBool, - .offset = offsetof(GPBMethod__storage_, requestStreaming), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "responseTypeURL", + .dataTypeSpecific.className = NULL, .number = GPBMethod_FieldNumber_ResponseTypeURL, - .hasIndex = 3, + .hasIndex = 4, + .offset = (uint32_t)offsetof(GPBMethod__storage_, responseTypeURL), .flags = GPBFieldOptional | GPBFieldTextFormatNameCustom, .dataType = GPBDataTypeString, - .offset = offsetof(GPBMethod__storage_, responseTypeURL), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "responseStreaming", + .dataTypeSpecific.className = NULL, .number = GPBMethod_FieldNumber_ResponseStreaming, - .hasIndex = 4, + .hasIndex = 5, + .offset = 6, // Stored in _has_storage_ to save space. .flags = GPBFieldOptional, .dataType = GPBDataTypeBool, - .offset = offsetof(GPBMethod__storage_, responseStreaming), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "optionsArray", + .dataTypeSpecific.className = GPBStringifySymbol(GPBOption), .number = GPBMethod_FieldNumber_OptionsArray, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBMethod__storage_, optionsArray), .flags = GPBFieldRepeated, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBMethod__storage_, optionsArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBOption), - .fieldOptions = NULL, }, { .name = "syntax", + .dataTypeSpecific.enumDescFunc = GPBSyntax_EnumDescriptor, .number = GPBMethod_FieldNumber_Syntax, - .hasIndex = 6, + .hasIndex = 7, + .offset = (uint32_t)offsetof(GPBMethod__storage_, syntax), .flags = GPBFieldOptional | GPBFieldHasEnumDescriptor, .dataType = GPBDataTypeEnum, - .offset = offsetof(GPBMethod__storage_, syntax), - .defaultValue.valueEnum = GPBSyntax_SyntaxProto2, - .dataTypeSpecific.enumDescFunc = GPBSyntax_EnumDescriptor, - .fieldOptions = NULL, }, }; -#if GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS - const char *extraTextFormatInfo = NULL; -#else - static const char *extraTextFormatInfo = "\002\002\007\244\241!!\000\004\010\244\241!!\000"; -#endif // GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS GPBDescriptor *localDescriptor = [GPBDescriptor allocDescriptorForClass:[GPBMethod class] rootClass:[GPBApiRoot class] file:GPBApiRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBMethod__storage_) - wireFormat:NO - extraTextFormatInfo:extraTextFormatInfo]; + flags:0]; +#if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS + static const char *extraTextFormatInfo = + "\002\002\007\244\241!!\000\004\010\244\241!!\000"; + [localDescriptor setupExtraTextInfo:extraTextFormatInfo]; +#endif // !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -349,25 +309,21 @@ typedef struct GPBMixin__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "name", + .dataTypeSpecific.className = NULL, .number = GPBMixin_FieldNumber_Name, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBMixin__storage_, name), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBMixin__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "root", + .dataTypeSpecific.className = NULL, .number = GPBMixin_FieldNumber_Root, .hasIndex = 1, + .offset = (uint32_t)offsetof(GPBMixin__storage_, root), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBMixin__storage_, root), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -375,15 +331,9 @@ typedef struct GPBMixin__storage_ { rootClass:[GPBApiRoot class] file:GPBApiRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBMixin__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -393,4 +343,6 @@ typedef struct GPBMixin__storage_ { @end +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Descriptor.pbobjc.h b/objectivec/google/protobuf/Descriptor.pbobjc.h deleted file mode 100644 index 109711c78..000000000 --- a/objectivec/google/protobuf/Descriptor.pbobjc.h +++ /dev/null @@ -1,1279 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: google/protobuf/descriptor.proto - -#import "GPBProtocolBuffers.h" - -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 -#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. -#endif - -// @@protoc_insertion_point(imports) - -CF_EXTERN_C_BEGIN - -@class GPBDescriptorProto; -@class GPBDescriptorProto_ExtensionRange; -@class GPBDescriptorProto_ReservedRange; -@class GPBEnumDescriptorProto; -@class GPBEnumOptions; -@class GPBEnumValueDescriptorProto; -@class GPBEnumValueOptions; -@class GPBFieldDescriptorProto; -@class GPBFieldOptions; -@class GPBFileDescriptorProto; -@class GPBFileOptions; -@class GPBGeneratedCodeInfo_Annotation; -@class GPBMessageOptions; -@class GPBMethodDescriptorProto; -@class GPBMethodOptions; -@class GPBOneofDescriptorProto; -@class GPBServiceDescriptorProto; -@class GPBServiceOptions; -@class GPBSourceCodeInfo; -@class GPBSourceCodeInfo_Location; -@class GPBUninterpretedOption; -@class GPBUninterpretedOption_NamePart; - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - Enum GPBFieldDescriptorProto_Type - -typedef GPB_ENUM(GPBFieldDescriptorProto_Type) { - /// 0 is reserved for errors. - /// Order is weird for historical reasons. - GPBFieldDescriptorProto_Type_TypeDouble = 1, - GPBFieldDescriptorProto_Type_TypeFloat = 2, - - /// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if - /// negative values are likely. - GPBFieldDescriptorProto_Type_TypeInt64 = 3, - GPBFieldDescriptorProto_Type_TypeUint64 = 4, - - /// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if - /// negative values are likely. - GPBFieldDescriptorProto_Type_TypeInt32 = 5, - GPBFieldDescriptorProto_Type_TypeFixed64 = 6, - GPBFieldDescriptorProto_Type_TypeFixed32 = 7, - GPBFieldDescriptorProto_Type_TypeBool = 8, - GPBFieldDescriptorProto_Type_TypeString = 9, - - /// Tag-delimited aggregate. - GPBFieldDescriptorProto_Type_TypeGroup = 10, - - /// Length-delimited aggregate. - GPBFieldDescriptorProto_Type_TypeMessage = 11, - - /// New in version 2. - GPBFieldDescriptorProto_Type_TypeBytes = 12, - GPBFieldDescriptorProto_Type_TypeUint32 = 13, - GPBFieldDescriptorProto_Type_TypeEnum = 14, - GPBFieldDescriptorProto_Type_TypeSfixed32 = 15, - GPBFieldDescriptorProto_Type_TypeSfixed64 = 16, - - /// Uses ZigZag encoding. - GPBFieldDescriptorProto_Type_TypeSint32 = 17, - - /// Uses ZigZag encoding. - GPBFieldDescriptorProto_Type_TypeSint64 = 18, -}; - -GPBEnumDescriptor *GPBFieldDescriptorProto_Type_EnumDescriptor(void); - -/// Checks to see if the given value is defined by the enum or was not known at -/// the time this source was generated. -BOOL GPBFieldDescriptorProto_Type_IsValidValue(int32_t value); - -#pragma mark - Enum GPBFieldDescriptorProto_Label - -typedef GPB_ENUM(GPBFieldDescriptorProto_Label) { - /// 0 is reserved for errors - GPBFieldDescriptorProto_Label_LabelOptional = 1, - GPBFieldDescriptorProto_Label_LabelRequired = 2, - - /// TODO(sanjay): Should we add LABEL_MAP? - GPBFieldDescriptorProto_Label_LabelRepeated = 3, -}; - -GPBEnumDescriptor *GPBFieldDescriptorProto_Label_EnumDescriptor(void); - -/// Checks to see if the given value is defined by the enum or was not known at -/// the time this source was generated. -BOOL GPBFieldDescriptorProto_Label_IsValidValue(int32_t value); - -#pragma mark - Enum GPBFileOptions_OptimizeMode - -/// Generated classes can be optimized for speed or code size. -typedef GPB_ENUM(GPBFileOptions_OptimizeMode) { - /// Generate complete code for parsing, serialization, - GPBFileOptions_OptimizeMode_Speed = 1, - - /// etc. - GPBFileOptions_OptimizeMode_CodeSize = 2, - - /// Generate code using MessageLite and the lite runtime. - GPBFileOptions_OptimizeMode_LiteRuntime = 3, -}; - -GPBEnumDescriptor *GPBFileOptions_OptimizeMode_EnumDescriptor(void); - -/// Checks to see if the given value is defined by the enum or was not known at -/// the time this source was generated. -BOOL GPBFileOptions_OptimizeMode_IsValidValue(int32_t value); - -#pragma mark - Enum GPBFieldOptions_CType - -typedef GPB_ENUM(GPBFieldOptions_CType) { - /// Default mode. - GPBFieldOptions_CType_String = 0, - GPBFieldOptions_CType_Cord = 1, - GPBFieldOptions_CType_StringPiece = 2, -}; - -GPBEnumDescriptor *GPBFieldOptions_CType_EnumDescriptor(void); - -/// Checks to see if the given value is defined by the enum or was not known at -/// the time this source was generated. -BOOL GPBFieldOptions_CType_IsValidValue(int32_t value); - -#pragma mark - Enum GPBFieldOptions_JSType - -typedef GPB_ENUM(GPBFieldOptions_JSType) { - /// Use the default type. - GPBFieldOptions_JSType_JsNormal = 0, - - /// Use JavaScript strings. - GPBFieldOptions_JSType_JsString = 1, - - /// Use JavaScript numbers. - GPBFieldOptions_JSType_JsNumber = 2, -}; - -GPBEnumDescriptor *GPBFieldOptions_JSType_EnumDescriptor(void); - -/// Checks to see if the given value is defined by the enum or was not known at -/// the time this source was generated. -BOOL GPBFieldOptions_JSType_IsValidValue(int32_t value); - -#pragma mark - GPBDescriptorRoot - -/// Exposes the extension registry for this file. -/// -/// The base class provides: -/// @code -/// + (GPBExtensionRegistry *)extensionRegistry; -/// @endcode -/// which is a @c GPBExtensionRegistry that includes all the extensions defined by -/// this file and all files that it depends on. -@interface GPBDescriptorRoot : GPBRootObject -@end - -#pragma mark - GPBFileDescriptorSet - -typedef GPB_ENUM(GPBFileDescriptorSet_FieldNumber) { - GPBFileDescriptorSet_FieldNumber_FileArray = 1, -}; - -/// The protocol compiler can output a FileDescriptorSet containing the .proto -/// files it parses. -@interface GPBFileDescriptorSet : GPBMessage - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *fileArray; -/// The number of items in @c fileArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger fileArray_Count; - -@end - -#pragma mark - GPBFileDescriptorProto - -typedef GPB_ENUM(GPBFileDescriptorProto_FieldNumber) { - GPBFileDescriptorProto_FieldNumber_Name = 1, - GPBFileDescriptorProto_FieldNumber_Package = 2, - GPBFileDescriptorProto_FieldNumber_DependencyArray = 3, - GPBFileDescriptorProto_FieldNumber_MessageTypeArray = 4, - GPBFileDescriptorProto_FieldNumber_EnumTypeArray = 5, - GPBFileDescriptorProto_FieldNumber_ServiceArray = 6, - GPBFileDescriptorProto_FieldNumber_ExtensionArray = 7, - GPBFileDescriptorProto_FieldNumber_Options = 8, - GPBFileDescriptorProto_FieldNumber_SourceCodeInfo = 9, - GPBFileDescriptorProto_FieldNumber_PublicDependencyArray = 10, - GPBFileDescriptorProto_FieldNumber_WeakDependencyArray = 11, - GPBFileDescriptorProto_FieldNumber_Syntax = 12, -}; - -/// Describes a complete .proto file. -@interface GPBFileDescriptorProto : GPBMessage - -/// file name, relative to root of source tree -@property(nonatomic, readwrite, copy, null_resettable) NSString *name; -/// Test to see if @c name has been set. -@property(nonatomic, readwrite) BOOL hasName; - -/// e.g. "foo", "foo.bar", etc. -@property(nonatomic, readwrite, copy, null_resettable) NSString *package; -/// Test to see if @c package has been set. -@property(nonatomic, readwrite) BOOL hasPackage; - -/// Names of files imported by this file. -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *dependencyArray; -/// The number of items in @c dependencyArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger dependencyArray_Count; - -/// Indexes of the public imported files in the dependency list above. -@property(nonatomic, readwrite, strong, null_resettable) GPBInt32Array *publicDependencyArray; -/// The number of items in @c publicDependencyArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger publicDependencyArray_Count; - -/// Indexes of the weak imported files in the dependency list. -/// For Google-internal migration only. Do not use. -@property(nonatomic, readwrite, strong, null_resettable) GPBInt32Array *weakDependencyArray; -/// The number of items in @c weakDependencyArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger weakDependencyArray_Count; - -/// All top-level definitions in this file. -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *messageTypeArray; -/// The number of items in @c messageTypeArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger messageTypeArray_Count; - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *enumTypeArray; -/// The number of items in @c enumTypeArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger enumTypeArray_Count; - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *serviceArray; -/// The number of items in @c serviceArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger serviceArray_Count; - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *extensionArray; -/// The number of items in @c extensionArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger extensionArray_Count; - -@property(nonatomic, readwrite, strong, null_resettable) GPBFileOptions *options; -/// Test to see if @c options has been set. -@property(nonatomic, readwrite) BOOL hasOptions; - -/// This field contains optional information about the original source code. -/// You may safely remove this entire field without harming runtime -/// functionality of the descriptors -- the information is needed only by -/// development tools. -@property(nonatomic, readwrite, strong, null_resettable) GPBSourceCodeInfo *sourceCodeInfo; -/// Test to see if @c sourceCodeInfo has been set. -@property(nonatomic, readwrite) BOOL hasSourceCodeInfo; - -/// The syntax of the proto file. -/// The supported values are "proto2" and "proto3". -@property(nonatomic, readwrite, copy, null_resettable) NSString *syntax; -/// Test to see if @c syntax has been set. -@property(nonatomic, readwrite) BOOL hasSyntax; - -@end - -#pragma mark - GPBDescriptorProto - -typedef GPB_ENUM(GPBDescriptorProto_FieldNumber) { - GPBDescriptorProto_FieldNumber_Name = 1, - GPBDescriptorProto_FieldNumber_FieldArray = 2, - GPBDescriptorProto_FieldNumber_NestedTypeArray = 3, - GPBDescriptorProto_FieldNumber_EnumTypeArray = 4, - GPBDescriptorProto_FieldNumber_ExtensionRangeArray = 5, - GPBDescriptorProto_FieldNumber_ExtensionArray = 6, - GPBDescriptorProto_FieldNumber_Options = 7, - GPBDescriptorProto_FieldNumber_OneofDeclArray = 8, - GPBDescriptorProto_FieldNumber_ReservedRangeArray = 9, - GPBDescriptorProto_FieldNumber_ReservedNameArray = 10, -}; - -/// Describes a message type. -@interface GPBDescriptorProto : GPBMessage - -@property(nonatomic, readwrite, copy, null_resettable) NSString *name; -/// Test to see if @c name has been set. -@property(nonatomic, readwrite) BOOL hasName; - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *fieldArray; -/// The number of items in @c fieldArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger fieldArray_Count; - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *extensionArray; -/// The number of items in @c extensionArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger extensionArray_Count; - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *nestedTypeArray; -/// The number of items in @c nestedTypeArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger nestedTypeArray_Count; - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *enumTypeArray; -/// The number of items in @c enumTypeArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger enumTypeArray_Count; - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *extensionRangeArray; -/// The number of items in @c extensionRangeArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger extensionRangeArray_Count; - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *oneofDeclArray; -/// The number of items in @c oneofDeclArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger oneofDeclArray_Count; - -@property(nonatomic, readwrite, strong, null_resettable) GPBMessageOptions *options; -/// Test to see if @c options has been set. -@property(nonatomic, readwrite) BOOL hasOptions; - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *reservedRangeArray; -/// The number of items in @c reservedRangeArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger reservedRangeArray_Count; - -/// Reserved field names, which may not be used by fields in the same message. -/// A given name may only be reserved once. -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *reservedNameArray; -/// The number of items in @c reservedNameArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger reservedNameArray_Count; - -@end - -#pragma mark - GPBDescriptorProto_ExtensionRange - -typedef GPB_ENUM(GPBDescriptorProto_ExtensionRange_FieldNumber) { - GPBDescriptorProto_ExtensionRange_FieldNumber_Start = 1, - GPBDescriptorProto_ExtensionRange_FieldNumber_End = 2, -}; - -@interface GPBDescriptorProto_ExtensionRange : GPBMessage - -@property(nonatomic, readwrite) int32_t start; - -@property(nonatomic, readwrite) BOOL hasStart; -@property(nonatomic, readwrite) int32_t end; - -@property(nonatomic, readwrite) BOOL hasEnd; -@end - -#pragma mark - GPBDescriptorProto_ReservedRange - -typedef GPB_ENUM(GPBDescriptorProto_ReservedRange_FieldNumber) { - GPBDescriptorProto_ReservedRange_FieldNumber_Start = 1, - GPBDescriptorProto_ReservedRange_FieldNumber_End = 2, -}; - -/// Range of reserved tag numbers. Reserved tag numbers may not be used by -/// fields or extension ranges in the same message. Reserved ranges may -/// not overlap. -@interface GPBDescriptorProto_ReservedRange : GPBMessage - -/// Inclusive. -@property(nonatomic, readwrite) int32_t start; - -@property(nonatomic, readwrite) BOOL hasStart; -/// Exclusive. -@property(nonatomic, readwrite) int32_t end; - -@property(nonatomic, readwrite) BOOL hasEnd; -@end - -#pragma mark - GPBFieldDescriptorProto - -typedef GPB_ENUM(GPBFieldDescriptorProto_FieldNumber) { - GPBFieldDescriptorProto_FieldNumber_Name = 1, - GPBFieldDescriptorProto_FieldNumber_Extendee = 2, - GPBFieldDescriptorProto_FieldNumber_Number = 3, - GPBFieldDescriptorProto_FieldNumber_Label = 4, - GPBFieldDescriptorProto_FieldNumber_Type = 5, - GPBFieldDescriptorProto_FieldNumber_TypeName = 6, - GPBFieldDescriptorProto_FieldNumber_DefaultValue = 7, - GPBFieldDescriptorProto_FieldNumber_Options = 8, - GPBFieldDescriptorProto_FieldNumber_OneofIndex = 9, - GPBFieldDescriptorProto_FieldNumber_JsonName = 10, -}; - -/// Describes a field within a message. -@interface GPBFieldDescriptorProto : GPBMessage - -@property(nonatomic, readwrite, copy, null_resettable) NSString *name; -/// Test to see if @c name has been set. -@property(nonatomic, readwrite) BOOL hasName; - -@property(nonatomic, readwrite) int32_t number; - -@property(nonatomic, readwrite) BOOL hasNumber; -@property(nonatomic, readwrite) GPBFieldDescriptorProto_Label label; - -@property(nonatomic, readwrite) BOOL hasLabel; -/// If type_name is set, this need not be set. If both this and type_name -/// are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. -@property(nonatomic, readwrite) GPBFieldDescriptorProto_Type type; - -@property(nonatomic, readwrite) BOOL hasType; -/// For message and enum types, this is the name of the type. If the name -/// starts with a '.', it is fully-qualified. Otherwise, C++-like scoping -/// rules are used to find the type (i.e. first the nested types within this -/// message are searched, then within the parent, on up to the root -/// namespace). -@property(nonatomic, readwrite, copy, null_resettable) NSString *typeName; -/// Test to see if @c typeName has been set. -@property(nonatomic, readwrite) BOOL hasTypeName; - -/// For extensions, this is the name of the type being extended. It is -/// resolved in the same manner as type_name. -@property(nonatomic, readwrite, copy, null_resettable) NSString *extendee; -/// Test to see if @c extendee has been set. -@property(nonatomic, readwrite) BOOL hasExtendee; - -/// For numeric types, contains the original text representation of the value. -/// For booleans, "true" or "false". -/// For strings, contains the default text contents (not escaped in any way). -/// For bytes, contains the C escaped value. All bytes >= 128 are escaped. -/// TODO(kenton): Base-64 encode? -@property(nonatomic, readwrite, copy, null_resettable) NSString *defaultValue; -/// Test to see if @c defaultValue has been set. -@property(nonatomic, readwrite) BOOL hasDefaultValue; - -/// If set, gives the index of a oneof in the containing type's oneof_decl -/// list. This field is a member of that oneof. -@property(nonatomic, readwrite) int32_t oneofIndex; - -@property(nonatomic, readwrite) BOOL hasOneofIndex; -/// JSON name of this field. The value is set by protocol compiler. If the -/// user has set a "json_name" option on this field, that option's value -/// will be used. Otherwise, it's deduced from the field's name by converting -/// it to camelCase. -@property(nonatomic, readwrite, copy, null_resettable) NSString *jsonName; -/// Test to see if @c jsonName has been set. -@property(nonatomic, readwrite) BOOL hasJsonName; - -@property(nonatomic, readwrite, strong, null_resettable) GPBFieldOptions *options; -/// Test to see if @c options has been set. -@property(nonatomic, readwrite) BOOL hasOptions; - -@end - -#pragma mark - GPBOneofDescriptorProto - -typedef GPB_ENUM(GPBOneofDescriptorProto_FieldNumber) { - GPBOneofDescriptorProto_FieldNumber_Name = 1, -}; - -/// Describes a oneof. -@interface GPBOneofDescriptorProto : GPBMessage - -@property(nonatomic, readwrite, copy, null_resettable) NSString *name; -/// Test to see if @c name has been set. -@property(nonatomic, readwrite) BOOL hasName; - -@end - -#pragma mark - GPBEnumDescriptorProto - -typedef GPB_ENUM(GPBEnumDescriptorProto_FieldNumber) { - GPBEnumDescriptorProto_FieldNumber_Name = 1, - GPBEnumDescriptorProto_FieldNumber_ValueArray = 2, - GPBEnumDescriptorProto_FieldNumber_Options = 3, -}; - -/// Describes an enum type. -@interface GPBEnumDescriptorProto : GPBMessage - -@property(nonatomic, readwrite, copy, null_resettable) NSString *name; -/// Test to see if @c name has been set. -@property(nonatomic, readwrite) BOOL hasName; - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *valueArray; -/// The number of items in @c valueArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger valueArray_Count; - -@property(nonatomic, readwrite, strong, null_resettable) GPBEnumOptions *options; -/// Test to see if @c options has been set. -@property(nonatomic, readwrite) BOOL hasOptions; - -@end - -#pragma mark - GPBEnumValueDescriptorProto - -typedef GPB_ENUM(GPBEnumValueDescriptorProto_FieldNumber) { - GPBEnumValueDescriptorProto_FieldNumber_Name = 1, - GPBEnumValueDescriptorProto_FieldNumber_Number = 2, - GPBEnumValueDescriptorProto_FieldNumber_Options = 3, -}; - -/// Describes a value within an enum. -@interface GPBEnumValueDescriptorProto : GPBMessage - -@property(nonatomic, readwrite, copy, null_resettable) NSString *name; -/// Test to see if @c name has been set. -@property(nonatomic, readwrite) BOOL hasName; - -@property(nonatomic, readwrite) int32_t number; - -@property(nonatomic, readwrite) BOOL hasNumber; -@property(nonatomic, readwrite, strong, null_resettable) GPBEnumValueOptions *options; -/// Test to see if @c options has been set. -@property(nonatomic, readwrite) BOOL hasOptions; - -@end - -#pragma mark - GPBServiceDescriptorProto - -typedef GPB_ENUM(GPBServiceDescriptorProto_FieldNumber) { - GPBServiceDescriptorProto_FieldNumber_Name = 1, - GPBServiceDescriptorProto_FieldNumber_MethodArray = 2, - GPBServiceDescriptorProto_FieldNumber_Options = 3, -}; - -/// Describes a service. -@interface GPBServiceDescriptorProto : GPBMessage - -@property(nonatomic, readwrite, copy, null_resettable) NSString *name; -/// Test to see if @c name has been set. -@property(nonatomic, readwrite) BOOL hasName; - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *methodArray; -/// The number of items in @c methodArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger methodArray_Count; - -@property(nonatomic, readwrite, strong, null_resettable) GPBServiceOptions *options; -/// Test to see if @c options has been set. -@property(nonatomic, readwrite) BOOL hasOptions; - -@end - -#pragma mark - GPBMethodDescriptorProto - -typedef GPB_ENUM(GPBMethodDescriptorProto_FieldNumber) { - GPBMethodDescriptorProto_FieldNumber_Name = 1, - GPBMethodDescriptorProto_FieldNumber_InputType = 2, - GPBMethodDescriptorProto_FieldNumber_OutputType = 3, - GPBMethodDescriptorProto_FieldNumber_Options = 4, - GPBMethodDescriptorProto_FieldNumber_ClientStreaming = 5, - GPBMethodDescriptorProto_FieldNumber_ServerStreaming = 6, -}; - -/// Describes a method of a service. -@interface GPBMethodDescriptorProto : GPBMessage - -@property(nonatomic, readwrite, copy, null_resettable) NSString *name; -/// Test to see if @c name has been set. -@property(nonatomic, readwrite) BOOL hasName; - -/// Input and output type names. These are resolved in the same way as -/// FieldDescriptorProto.type_name, but must refer to a message type. -@property(nonatomic, readwrite, copy, null_resettable) NSString *inputType; -/// Test to see if @c inputType has been set. -@property(nonatomic, readwrite) BOOL hasInputType; - -@property(nonatomic, readwrite, copy, null_resettable) NSString *outputType; -/// Test to see if @c outputType has been set. -@property(nonatomic, readwrite) BOOL hasOutputType; - -@property(nonatomic, readwrite, strong, null_resettable) GPBMethodOptions *options; -/// Test to see if @c options has been set. -@property(nonatomic, readwrite) BOOL hasOptions; - -/// Identifies if client streams multiple client messages -@property(nonatomic, readwrite) BOOL clientStreaming; - -@property(nonatomic, readwrite) BOOL hasClientStreaming; -/// Identifies if server streams multiple server messages -@property(nonatomic, readwrite) BOOL serverStreaming; - -@property(nonatomic, readwrite) BOOL hasServerStreaming; -@end - -#pragma mark - GPBFileOptions - -typedef GPB_ENUM(GPBFileOptions_FieldNumber) { - GPBFileOptions_FieldNumber_JavaPackage = 1, - GPBFileOptions_FieldNumber_JavaOuterClassname = 8, - GPBFileOptions_FieldNumber_OptimizeFor = 9, - GPBFileOptions_FieldNumber_JavaMultipleFiles = 10, - GPBFileOptions_FieldNumber_GoPackage = 11, - GPBFileOptions_FieldNumber_CcGenericServices = 16, - GPBFileOptions_FieldNumber_JavaGenericServices = 17, - GPBFileOptions_FieldNumber_PyGenericServices = 18, - GPBFileOptions_FieldNumber_JavaGenerateEqualsAndHash = 20, - GPBFileOptions_FieldNumber_Deprecated = 23, - GPBFileOptions_FieldNumber_JavaStringCheckUtf8 = 27, - GPBFileOptions_FieldNumber_CcEnableArenas = 31, - GPBFileOptions_FieldNumber_ObjcClassPrefix = 36, - GPBFileOptions_FieldNumber_CsharpNamespace = 37, - GPBFileOptions_FieldNumber_JavananoUseDeprecatedPackage = 38, - GPBFileOptions_FieldNumber_UninterpretedOptionArray = 999, -}; - -@interface GPBFileOptions : GPBMessage - -/// Sets the Java package where classes generated from this .proto will be -/// placed. By default, the proto package is used, but this is often -/// inappropriate because proto packages do not normally start with backwards -/// domain names. -@property(nonatomic, readwrite, copy, null_resettable) NSString *javaPackage; -/// Test to see if @c javaPackage has been set. -@property(nonatomic, readwrite) BOOL hasJavaPackage; - -/// If set, all the classes from the .proto file are wrapped in a single -/// outer class with the given name. This applies to both Proto1 -/// (equivalent to the old "--one_java_file" option) and Proto2 (where -/// a .proto always translates to a single class, but you may want to -/// explicitly choose the class name). -@property(nonatomic, readwrite, copy, null_resettable) NSString *javaOuterClassname; -/// Test to see if @c javaOuterClassname has been set. -@property(nonatomic, readwrite) BOOL hasJavaOuterClassname; - -/// If set true, then the Java code generator will generate a separate .java -/// file for each top-level message, enum, and service defined in the .proto -/// file. Thus, these types will *not* be nested inside the outer class -/// named by java_outer_classname. However, the outer class will still be -/// generated to contain the file's getDescriptor() method as well as any -/// top-level extensions defined in the file. -@property(nonatomic, readwrite) BOOL javaMultipleFiles; - -@property(nonatomic, readwrite) BOOL hasJavaMultipleFiles; -/// If set true, then the Java code generator will generate equals() and -/// hashCode() methods for all messages defined in the .proto file. -/// This increases generated code size, potentially substantially for large -/// protos, which may harm a memory-constrained application. -/// - In the full runtime this is a speed optimization, as the -/// AbstractMessage base class includes reflection-based implementations of -/// these methods. -/// - In the lite runtime, setting this option changes the semantics of -/// equals() and hashCode() to more closely match those of the full runtime; -/// the generated methods compute their results based on field values rather -/// than object identity. (Implementations should not assume that hashcodes -/// will be consistent across runtimes or versions of the protocol compiler.) -@property(nonatomic, readwrite) BOOL javaGenerateEqualsAndHash; - -@property(nonatomic, readwrite) BOOL hasJavaGenerateEqualsAndHash; -/// If set true, then the Java2 code generator will generate code that -/// throws an exception whenever an attempt is made to assign a non-UTF-8 -/// byte sequence to a string field. -/// Message reflection will do the same. -/// However, an extension field still accepts non-UTF-8 byte sequences. -/// This option has no effect on when used with the lite runtime. -@property(nonatomic, readwrite) BOOL javaStringCheckUtf8; - -@property(nonatomic, readwrite) BOOL hasJavaStringCheckUtf8; -@property(nonatomic, readwrite) GPBFileOptions_OptimizeMode optimizeFor; - -@property(nonatomic, readwrite) BOOL hasOptimizeFor; -/// Sets the Go package where structs generated from this .proto will be -/// placed. If omitted, the Go package will be derived from the following: -/// - The basename of the package import path, if provided. -/// - Otherwise, the package statement in the .proto file, if present. -/// - Otherwise, the basename of the .proto file, without extension. -@property(nonatomic, readwrite, copy, null_resettable) NSString *goPackage; -/// Test to see if @c goPackage has been set. -@property(nonatomic, readwrite) BOOL hasGoPackage; - -/// Should generic services be generated in each language? "Generic" services -/// are not specific to any particular RPC system. They are generated by the -/// main code generators in each language (without additional plugins). -/// Generic services were the only kind of service generation supported by -/// early versions of google.protobuf. -/// -/// Generic services are now considered deprecated in favor of using plugins -/// that generate code specific to your particular RPC system. Therefore, -/// these default to false. Old code which depends on generic services should -/// explicitly set them to true. -@property(nonatomic, readwrite) BOOL ccGenericServices; - -@property(nonatomic, readwrite) BOOL hasCcGenericServices; -@property(nonatomic, readwrite) BOOL javaGenericServices; - -@property(nonatomic, readwrite) BOOL hasJavaGenericServices; -@property(nonatomic, readwrite) BOOL pyGenericServices; - -@property(nonatomic, readwrite) BOOL hasPyGenericServices; -/// Is this file deprecated? -/// Depending on the target platform, this can emit Deprecated annotations -/// for everything in the file, or it will be completely ignored; in the very -/// least, this is a formalization for deprecating files. -@property(nonatomic, readwrite) BOOL deprecated; - -@property(nonatomic, readwrite) BOOL hasDeprecated; -/// Enables the use of arenas for the proto messages in this file. This applies -/// only to generated classes for C++. -@property(nonatomic, readwrite) BOOL ccEnableArenas; - -@property(nonatomic, readwrite) BOOL hasCcEnableArenas; -/// Sets the objective c class prefix which is prepended to all objective c -/// generated classes from this .proto. There is no default. -@property(nonatomic, readwrite, copy, null_resettable) NSString *objcClassPrefix; -/// Test to see if @c objcClassPrefix has been set. -@property(nonatomic, readwrite) BOOL hasObjcClassPrefix; - -/// Namespace for generated classes; defaults to the package. -@property(nonatomic, readwrite, copy, null_resettable) NSString *csharpNamespace; -/// Test to see if @c csharpNamespace has been set. -@property(nonatomic, readwrite) BOOL hasCsharpNamespace; - -/// Whether the nano proto compiler should generate in the deprecated non-nano -/// suffixed package. -@property(nonatomic, readwrite) BOOL javananoUseDeprecatedPackage; - -@property(nonatomic, readwrite) BOOL hasJavananoUseDeprecatedPackage; -/// The parser stores options it doesn't recognize here. See above. -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray; -/// The number of items in @c uninterpretedOptionArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count; - -@end - -#pragma mark - GPBMessageOptions - -typedef GPB_ENUM(GPBMessageOptions_FieldNumber) { - GPBMessageOptions_FieldNumber_MessageSetWireFormat = 1, - GPBMessageOptions_FieldNumber_NoStandardDescriptorAccessor = 2, - GPBMessageOptions_FieldNumber_Deprecated = 3, - GPBMessageOptions_FieldNumber_MapEntry = 7, - GPBMessageOptions_FieldNumber_UninterpretedOptionArray = 999, -}; - -@interface GPBMessageOptions : GPBMessage - -/// Set true to use the old proto1 MessageSet wire format for extensions. -/// This is provided for backwards-compatibility with the MessageSet wire -/// format. You should not use this for any other reason: It's less -/// efficient, has fewer features, and is more complicated. -/// -/// The message must be defined exactly as follows: -/// message Foo { -/// option message_set_wire_format = true; -/// extensions 4 to max; -/// } -/// Note that the message cannot have any defined fields; MessageSets only -/// have extensions. -/// -/// All extensions of your type must be singular messages; e.g. they cannot -/// be int32s, enums, or repeated messages. -/// -/// Because this is an option, the above two restrictions are not enforced by -/// the protocol compiler. -@property(nonatomic, readwrite) BOOL messageSetWireFormat; - -@property(nonatomic, readwrite) BOOL hasMessageSetWireFormat; -/// Disables the generation of the standard "descriptor()" accessor, which can -/// conflict with a field of the same name. This is meant to make migration -/// from proto1 easier; new code should avoid fields named "descriptor". -@property(nonatomic, readwrite) BOOL noStandardDescriptorAccessor; - -@property(nonatomic, readwrite) BOOL hasNoStandardDescriptorAccessor; -/// Is this message deprecated? -/// Depending on the target platform, this can emit Deprecated annotations -/// for the message, or it will be completely ignored; in the very least, -/// this is a formalization for deprecating messages. -@property(nonatomic, readwrite) BOOL deprecated; - -@property(nonatomic, readwrite) BOOL hasDeprecated; -/// Whether the message is an automatically generated map entry type for the -/// maps field. -/// -/// For maps fields: -/// map map_field = 1; -/// The parsed descriptor looks like: -/// message MapFieldEntry { -/// option map_entry = true; -/// optional KeyType key = 1; -/// optional ValueType value = 2; -/// } -/// repeated MapFieldEntry map_field = 1; -/// -/// Implementations may choose not to generate the map_entry=true message, but -/// use a native map in the target language to hold the keys and values. -/// The reflection APIs in such implementions still need to work as -/// if the field is a repeated message field. -/// -/// NOTE: Do not set the option in .proto files. Always use the maps syntax -/// instead. The option should only be implicitly set by the proto compiler -/// parser. -@property(nonatomic, readwrite) BOOL mapEntry; - -@property(nonatomic, readwrite) BOOL hasMapEntry; -/// The parser stores options it doesn't recognize here. See above. -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray; -/// The number of items in @c uninterpretedOptionArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count; - -@end - -#pragma mark - GPBFieldOptions - -typedef GPB_ENUM(GPBFieldOptions_FieldNumber) { - GPBFieldOptions_FieldNumber_Ctype = 1, - GPBFieldOptions_FieldNumber_Packed = 2, - GPBFieldOptions_FieldNumber_Deprecated = 3, - GPBFieldOptions_FieldNumber_Lazy = 5, - GPBFieldOptions_FieldNumber_Jstype = 6, - GPBFieldOptions_FieldNumber_Weak = 10, - GPBFieldOptions_FieldNumber_UninterpretedOptionArray = 999, -}; - -@interface GPBFieldOptions : GPBMessage - -/// The ctype option instructs the C++ code generator to use a different -/// representation of the field than it normally would. See the specific -/// options below. This option is not yet implemented in the open source -/// release -- sorry, we'll try to include it in a future version! -@property(nonatomic, readwrite) GPBFieldOptions_CType ctype; - -@property(nonatomic, readwrite) BOOL hasCtype; -/// The packed option can be enabled for repeated primitive fields to enable -/// a more efficient representation on the wire. Rather than repeatedly -/// writing the tag and type for each element, the entire array is encoded as -/// a single length-delimited blob. In proto3, only explicit setting it to -/// false will avoid using packed encoding. -@property(nonatomic, readwrite) BOOL packed; - -@property(nonatomic, readwrite) BOOL hasPacked; -/// The jstype option determines the JavaScript type used for values of the -/// field. The option is permitted only for 64 bit integral and fixed types -/// (int64, uint64, sint64, fixed64, sfixed64). By default these types are -/// represented as JavaScript strings. This avoids loss of precision that can -/// happen when a large value is converted to a floating point JavaScript -/// numbers. Specifying JS_NUMBER for the jstype causes the generated -/// JavaScript code to use the JavaScript "number" type instead of strings. -/// This option is an enum to permit additional types to be added, -/// e.g. goog.math.Integer. -@property(nonatomic, readwrite) GPBFieldOptions_JSType jstype; - -@property(nonatomic, readwrite) BOOL hasJstype; -/// Should this field be parsed lazily? Lazy applies only to message-type -/// fields. It means that when the outer message is initially parsed, the -/// inner message's contents will not be parsed but instead stored in encoded -/// form. The inner message will actually be parsed when it is first accessed. -/// -/// This is only a hint. Implementations are free to choose whether to use -/// eager or lazy parsing regardless of the value of this option. However, -/// setting this option true suggests that the protocol author believes that -/// using lazy parsing on this field is worth the additional bookkeeping -/// overhead typically needed to implement it. -/// -/// This option does not affect the public interface of any generated code; -/// all method signatures remain the same. Furthermore, thread-safety of the -/// interface is not affected by this option; const methods remain safe to -/// call from multiple threads concurrently, while non-const methods continue -/// to require exclusive access. -/// -/// -/// Note that implementations may choose not to check required fields within -/// a lazy sub-message. That is, calling IsInitialized() on the outher message -/// may return true even if the inner message has missing required fields. -/// This is necessary because otherwise the inner message would have to be -/// parsed in order to perform the check, defeating the purpose of lazy -/// parsing. An implementation which chooses not to check required fields -/// must be consistent about it. That is, for any particular sub-message, the -/// implementation must either *always* check its required fields, or *never* -/// check its required fields, regardless of whether or not the message has -/// been parsed. -@property(nonatomic, readwrite) BOOL lazy; - -@property(nonatomic, readwrite) BOOL hasLazy; -/// Is this field deprecated? -/// Depending on the target platform, this can emit Deprecated annotations -/// for accessors, or it will be completely ignored; in the very least, this -/// is a formalization for deprecating fields. -@property(nonatomic, readwrite) BOOL deprecated; - -@property(nonatomic, readwrite) BOOL hasDeprecated; -/// For Google-internal migration only. Do not use. -@property(nonatomic, readwrite) BOOL weak; - -@property(nonatomic, readwrite) BOOL hasWeak; -/// The parser stores options it doesn't recognize here. See above. -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray; -/// The number of items in @c uninterpretedOptionArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count; - -@end - -#pragma mark - GPBEnumOptions - -typedef GPB_ENUM(GPBEnumOptions_FieldNumber) { - GPBEnumOptions_FieldNumber_AllowAlias = 2, - GPBEnumOptions_FieldNumber_Deprecated = 3, - GPBEnumOptions_FieldNumber_UninterpretedOptionArray = 999, -}; - -@interface GPBEnumOptions : GPBMessage - -/// Set this option to true to allow mapping different tag names to the same -/// value. -@property(nonatomic, readwrite) BOOL allowAlias; - -@property(nonatomic, readwrite) BOOL hasAllowAlias; -/// Is this enum deprecated? -/// Depending on the target platform, this can emit Deprecated annotations -/// for the enum, or it will be completely ignored; in the very least, this -/// is a formalization for deprecating enums. -@property(nonatomic, readwrite) BOOL deprecated; - -@property(nonatomic, readwrite) BOOL hasDeprecated; -/// The parser stores options it doesn't recognize here. See above. -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray; -/// The number of items in @c uninterpretedOptionArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count; - -@end - -#pragma mark - GPBEnumValueOptions - -typedef GPB_ENUM(GPBEnumValueOptions_FieldNumber) { - GPBEnumValueOptions_FieldNumber_Deprecated = 1, - GPBEnumValueOptions_FieldNumber_UninterpretedOptionArray = 999, -}; - -@interface GPBEnumValueOptions : GPBMessage - -/// Is this enum value deprecated? -/// Depending on the target platform, this can emit Deprecated annotations -/// for the enum value, or it will be completely ignored; in the very least, -/// this is a formalization for deprecating enum values. -@property(nonatomic, readwrite) BOOL deprecated; - -@property(nonatomic, readwrite) BOOL hasDeprecated; -/// The parser stores options it doesn't recognize here. See above. -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray; -/// The number of items in @c uninterpretedOptionArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count; - -@end - -#pragma mark - GPBServiceOptions - -typedef GPB_ENUM(GPBServiceOptions_FieldNumber) { - GPBServiceOptions_FieldNumber_Deprecated = 33, - GPBServiceOptions_FieldNumber_UninterpretedOptionArray = 999, -}; - -@interface GPBServiceOptions : GPBMessage - -/// Is this service deprecated? -/// Depending on the target platform, this can emit Deprecated annotations -/// for the service, or it will be completely ignored; in the very least, -/// this is a formalization for deprecating services. -@property(nonatomic, readwrite) BOOL deprecated; - -@property(nonatomic, readwrite) BOOL hasDeprecated; -/// The parser stores options it doesn't recognize here. See above. -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray; -/// The number of items in @c uninterpretedOptionArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count; - -@end - -#pragma mark - GPBMethodOptions - -typedef GPB_ENUM(GPBMethodOptions_FieldNumber) { - GPBMethodOptions_FieldNumber_Deprecated = 33, - GPBMethodOptions_FieldNumber_UninterpretedOptionArray = 999, -}; - -@interface GPBMethodOptions : GPBMessage - -/// Is this method deprecated? -/// Depending on the target platform, this can emit Deprecated annotations -/// for the method, or it will be completely ignored; in the very least, -/// this is a formalization for deprecating methods. -@property(nonatomic, readwrite) BOOL deprecated; - -@property(nonatomic, readwrite) BOOL hasDeprecated; -/// The parser stores options it doesn't recognize here. See above. -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *uninterpretedOptionArray; -/// The number of items in @c uninterpretedOptionArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger uninterpretedOptionArray_Count; - -@end - -#pragma mark - GPBUninterpretedOption - -typedef GPB_ENUM(GPBUninterpretedOption_FieldNumber) { - GPBUninterpretedOption_FieldNumber_NameArray = 2, - GPBUninterpretedOption_FieldNumber_IdentifierValue = 3, - GPBUninterpretedOption_FieldNumber_PositiveIntValue = 4, - GPBUninterpretedOption_FieldNumber_NegativeIntValue = 5, - GPBUninterpretedOption_FieldNumber_DoubleValue = 6, - GPBUninterpretedOption_FieldNumber_StringValue = 7, - GPBUninterpretedOption_FieldNumber_AggregateValue = 8, -}; - -/// A message representing a option the parser does not recognize. This only -/// appears in options protos created by the compiler::Parser class. -/// DescriptorPool resolves these when building Descriptor objects. Therefore, -/// options protos in descriptor objects (e.g. returned by Descriptor::options(), -/// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions -/// in them. -@interface GPBUninterpretedOption : GPBMessage - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *nameArray; -/// The number of items in @c nameArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger nameArray_Count; - -/// The value of the uninterpreted option, in whatever type the tokenizer -/// identified it as during parsing. Exactly one of these should be set. -@property(nonatomic, readwrite, copy, null_resettable) NSString *identifierValue; -/// Test to see if @c identifierValue has been set. -@property(nonatomic, readwrite) BOOL hasIdentifierValue; - -@property(nonatomic, readwrite) uint64_t positiveIntValue; - -@property(nonatomic, readwrite) BOOL hasPositiveIntValue; -@property(nonatomic, readwrite) int64_t negativeIntValue; - -@property(nonatomic, readwrite) BOOL hasNegativeIntValue; -@property(nonatomic, readwrite) double doubleValue; - -@property(nonatomic, readwrite) BOOL hasDoubleValue; -@property(nonatomic, readwrite, copy, null_resettable) NSData *stringValue; -/// Test to see if @c stringValue has been set. -@property(nonatomic, readwrite) BOOL hasStringValue; - -@property(nonatomic, readwrite, copy, null_resettable) NSString *aggregateValue; -/// Test to see if @c aggregateValue has been set. -@property(nonatomic, readwrite) BOOL hasAggregateValue; - -@end - -#pragma mark - GPBUninterpretedOption_NamePart - -typedef GPB_ENUM(GPBUninterpretedOption_NamePart_FieldNumber) { - GPBUninterpretedOption_NamePart_FieldNumber_NamePart = 1, - GPBUninterpretedOption_NamePart_FieldNumber_IsExtension = 2, -}; - -/// 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". -@interface GPBUninterpretedOption_NamePart : GPBMessage - -@property(nonatomic, readwrite, copy, null_resettable) NSString *namePart; -/// Test to see if @c namePart has been set. -@property(nonatomic, readwrite) BOOL hasNamePart; - -@property(nonatomic, readwrite) BOOL isExtension; - -@property(nonatomic, readwrite) BOOL hasIsExtension; -@end - -#pragma mark - GPBSourceCodeInfo - -typedef GPB_ENUM(GPBSourceCodeInfo_FieldNumber) { - GPBSourceCodeInfo_FieldNumber_LocationArray = 1, -}; - -/// Encapsulates information about the original source file from which a -/// FileDescriptorProto was generated. -@interface GPBSourceCodeInfo : GPBMessage - -/// A Location identifies a piece of source code in a .proto file which -/// corresponds to a particular definition. This information is intended -/// to be useful to IDEs, code indexers, documentation generators, and similar -/// tools. -/// -/// For example, say we have a file like: -/// message Foo { -/// optional string foo = 1; -/// } -/// Let's look at just the field definition: -/// optional string foo = 1; -/// ^ ^^ ^^ ^ ^^^ -/// a bc de f ghi -/// We have the following locations: -/// span path represents -/// [a,i) [ 4, 0, 2, 0 ] The whole field definition. -/// [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). -/// [c,d) [ 4, 0, 2, 0, 5 ] The type (string). -/// [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). -/// [g,h) [ 4, 0, 2, 0, 3 ] The number (1). -/// -/// Notes: -/// - A location may refer to a repeated field itself (i.e. not to any -/// particular index within it). This is used whenever a set of elements are -/// logically enclosed in a single code segment. For example, an entire -/// extend block (possibly containing multiple extension definitions) will -/// have an outer location whose path refers to the "extensions" repeated -/// field without an index. -/// - Multiple locations may have the same path. This happens when a single -/// logical declaration is spread out across multiple places. The most -/// obvious example is the "extend" block again -- there may be multiple -/// extend blocks in the same scope, each of which will have the same path. -/// - A location's span is not always a subset of its parent's span. For -/// example, the "extendee" of an extension declaration appears at the -/// beginning of the "extend" block and is shared by all extensions within -/// the block. -/// - Just because a location's span is a subset of some other location's span -/// does not mean that it is a descendent. For example, a "group" defines -/// both a type and a field in a single declaration. Thus, the locations -/// corresponding to the type and field and their components will overlap. -/// - Code which tries to interpret locations should probably be designed to -/// ignore those that it doesn't understand, as more types of locations could -/// be recorded in the future. -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *locationArray; -/// The number of items in @c locationArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger locationArray_Count; - -@end - -#pragma mark - GPBSourceCodeInfo_Location - -typedef GPB_ENUM(GPBSourceCodeInfo_Location_FieldNumber) { - GPBSourceCodeInfo_Location_FieldNumber_PathArray = 1, - GPBSourceCodeInfo_Location_FieldNumber_SpanArray = 2, - GPBSourceCodeInfo_Location_FieldNumber_LeadingComments = 3, - GPBSourceCodeInfo_Location_FieldNumber_TrailingComments = 4, - GPBSourceCodeInfo_Location_FieldNumber_LeadingDetachedCommentsArray = 6, -}; - -@interface GPBSourceCodeInfo_Location : GPBMessage - -/// Identifies which part of the FileDescriptorProto was defined at this -/// location. -/// -/// Each element is a field number or an index. They form a path from -/// the root FileDescriptorProto to the place where the definition. For -/// example, this path: -/// [ 4, 3, 2, 7, 1 ] -/// refers to: -/// file.message_type(3) // 4, 3 -/// .field(7) // 2, 7 -/// .name() // 1 -/// This is because FileDescriptorProto.message_type has field number 4: -/// repeated DescriptorProto message_type = 4; -/// and DescriptorProto.field has field number 2: -/// repeated FieldDescriptorProto field = 2; -/// and FieldDescriptorProto.name has field number 1: -/// optional string name = 1; -/// -/// Thus, the above path gives the location of a field name. If we removed -/// the last element: -/// [ 4, 3, 2, 7 ] -/// this path refers to the whole field declaration (from the beginning -/// of the label to the terminating semicolon). -@property(nonatomic, readwrite, strong, null_resettable) GPBInt32Array *pathArray; -/// The number of items in @c pathArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger pathArray_Count; - -/// Always has exactly three or four elements: start line, start column, -/// end line (optional, otherwise assumed same as start line), end column. -/// These are packed into a single field for efficiency. Note that line -/// and column numbers are zero-based -- typically you will want to add -/// 1 to each before displaying to a user. -@property(nonatomic, readwrite, strong, null_resettable) GPBInt32Array *spanArray; -/// The number of items in @c spanArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger spanArray_Count; - -/// If this SourceCodeInfo represents a complete declaration, these are any -/// comments appearing before and after the declaration which appear to be -/// attached to the declaration. -/// -/// A series of line comments appearing on consecutive lines, with no other -/// tokens appearing on those lines, will be treated as a single comment. -/// -/// leading_detached_comments will keep paragraphs of comments that appear -/// before (but not connected to) the current element. Each paragraph, -/// separated by empty lines, will be one comment element in the repeated -/// field. -/// -/// Only the comment content is provided; comment markers (e.g. //) are -/// stripped out. For block comments, leading whitespace and an asterisk -/// will be stripped from the beginning of each line other than the first. -/// Newlines are included in the output. -/// -/// Examples: -/// -/// optional int32 foo = 1; // Comment attached to foo. -/// // Comment attached to bar. -/// optional int32 bar = 2; -/// -/// optional string baz = 3; -/// // Comment attached to baz. -/// // Another line attached to baz. -/// -/// // Comment attached to qux. -/// // -/// // Another line attached to qux. -/// optional double qux = 4; -/// -/// // Detached comment for corge. This is not leading or trailing comments -/// // to qux or corge because there are blank lines separating it from -/// // both. -/// -/// // Detached comment for corge paragraph 2. -/// -/// optional string corge = 5; -/// /* Block comment attached -/// * to corge. Leading asterisks -/// * will be removed. */ -/// /* Block comment attached to -/// * grault. */ -/// optional int32 grault = 6; -/// -/// // ignored detached comments. -@property(nonatomic, readwrite, copy, null_resettable) NSString *leadingComments; -/// Test to see if @c leadingComments has been set. -@property(nonatomic, readwrite) BOOL hasLeadingComments; - -@property(nonatomic, readwrite, copy, null_resettable) NSString *trailingComments; -/// Test to see if @c trailingComments has been set. -@property(nonatomic, readwrite) BOOL hasTrailingComments; - -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *leadingDetachedCommentsArray; -/// The number of items in @c leadingDetachedCommentsArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger leadingDetachedCommentsArray_Count; - -@end - -#pragma mark - GPBGeneratedCodeInfo - -typedef GPB_ENUM(GPBGeneratedCodeInfo_FieldNumber) { - GPBGeneratedCodeInfo_FieldNumber_AnnotationArray = 1, -}; - -/// Describes the relationship between generated code and its original source -/// file. A GeneratedCodeInfo message is associated with only one generated -/// source file, but may contain references to different source .proto files. -@interface GPBGeneratedCodeInfo : GPBMessage - -/// An Annotation connects some span of text in generated code to an element -/// of its generating .proto file. -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *annotationArray; -/// The number of items in @c annotationArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger annotationArray_Count; - -@end - -#pragma mark - GPBGeneratedCodeInfo_Annotation - -typedef GPB_ENUM(GPBGeneratedCodeInfo_Annotation_FieldNumber) { - GPBGeneratedCodeInfo_Annotation_FieldNumber_PathArray = 1, - GPBGeneratedCodeInfo_Annotation_FieldNumber_SourceFile = 2, - GPBGeneratedCodeInfo_Annotation_FieldNumber_Begin = 3, - GPBGeneratedCodeInfo_Annotation_FieldNumber_End = 4, -}; - -@interface GPBGeneratedCodeInfo_Annotation : GPBMessage - -/// Identifies the element in the original source .proto file. This field -/// is formatted the same as SourceCodeInfo.Location.path. -@property(nonatomic, readwrite, strong, null_resettable) GPBInt32Array *pathArray; -/// The number of items in @c pathArray without causing the array to be created. -@property(nonatomic, readonly) NSUInteger pathArray_Count; - -/// Identifies the filesystem path to the original source .proto. -@property(nonatomic, readwrite, copy, null_resettable) NSString *sourceFile; -/// Test to see if @c sourceFile has been set. -@property(nonatomic, readwrite) BOOL hasSourceFile; - -/// Identifies the starting offset in bytes in the generated code -/// that relates to the identified object. -@property(nonatomic, readwrite) int32_t begin; - -@property(nonatomic, readwrite) BOOL hasBegin; -/// Identifies the ending offset in bytes in the generated code that -/// relates to the identified offset. The end offset should be one past -/// the last relevant byte (so the length of the text = end - begin). -@property(nonatomic, readwrite) int32_t end; - -@property(nonatomic, readwrite) BOOL hasEnd; -@end - -NS_ASSUME_NONNULL_END - -CF_EXTERN_C_END - -// @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Descriptor.pbobjc.m b/objectivec/google/protobuf/Descriptor.pbobjc.m deleted file mode 100644 index 403098931..000000000 --- a/objectivec/google/protobuf/Descriptor.pbobjc.m +++ /dev/null @@ -1,2594 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: google/protobuf/descriptor.proto - -#import "GPBProtocolBuffers_RuntimeSupport.h" -#import "google/protobuf/Descriptor.pbobjc.h" -// @@protoc_insertion_point(imports) - -#pragma mark - GPBDescriptorRoot - -@implementation GPBDescriptorRoot - -@end - -#pragma mark - GPBDescriptorRoot_FileDescriptor - -static GPBFileDescriptor *GPBDescriptorRoot_FileDescriptor(void) { - // This is called by +initialize so there is no need to worry - // about thread safety of the singleton. - static GPBFileDescriptor *descriptor = NULL; - if (!descriptor) { - GPBDebugCheckRuntimeVersion(); - descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"google.protobuf" - syntax:GPBFileSyntaxProto2]; - } - return descriptor; -} - -#pragma mark - GPBFileDescriptorSet - -@implementation GPBFileDescriptorSet - -@dynamic fileArray, fileArray_Count; - -typedef struct GPBFileDescriptorSet__storage_ { - uint32_t _has_storage_[1]; - NSMutableArray *fileArray; -} GPBFileDescriptorSet__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "fileArray", - .number = GPBFileDescriptorSet_FieldNumber_FileArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBFileDescriptorSet__storage_, fileArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBFileDescriptorProto), - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBFileDescriptorSet class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBFileDescriptorSet__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBFileDescriptorProto - -@implementation GPBFileDescriptorProto - -@dynamic hasName, name; -@dynamic hasPackage, package; -@dynamic dependencyArray, dependencyArray_Count; -@dynamic publicDependencyArray, publicDependencyArray_Count; -@dynamic weakDependencyArray, weakDependencyArray_Count; -@dynamic messageTypeArray, messageTypeArray_Count; -@dynamic enumTypeArray, enumTypeArray_Count; -@dynamic serviceArray, serviceArray_Count; -@dynamic extensionArray, extensionArray_Count; -@dynamic hasOptions, options; -@dynamic hasSourceCodeInfo, sourceCodeInfo; -@dynamic hasSyntax, syntax; - -typedef struct GPBFileDescriptorProto__storage_ { - uint32_t _has_storage_[1]; - NSString *name; - NSString *package; - NSMutableArray *dependencyArray; - NSMutableArray *messageTypeArray; - NSMutableArray *enumTypeArray; - NSMutableArray *serviceArray; - NSMutableArray *extensionArray; - GPBFileOptions *options; - GPBSourceCodeInfo *sourceCodeInfo; - GPBInt32Array *publicDependencyArray; - GPBInt32Array *weakDependencyArray; - NSString *syntax; -} GPBFileDescriptorProto__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "name", - .number = GPBFileDescriptorProto_FieldNumber_Name, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFileDescriptorProto__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "package", - .number = GPBFileDescriptorProto_FieldNumber_Package, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFileDescriptorProto__storage_, package), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "dependencyArray", - .number = GPBFileDescriptorProto_FieldNumber_DependencyArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFileDescriptorProto__storage_, dependencyArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "messageTypeArray", - .number = GPBFileDescriptorProto_FieldNumber_MessageTypeArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBFileDescriptorProto__storage_, messageTypeArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBDescriptorProto), - .fieldOptions = NULL, - }, - { - .name = "enumTypeArray", - .number = GPBFileDescriptorProto_FieldNumber_EnumTypeArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBFileDescriptorProto__storage_, enumTypeArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBEnumDescriptorProto), - .fieldOptions = NULL, - }, - { - .name = "serviceArray", - .number = GPBFileDescriptorProto_FieldNumber_ServiceArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBFileDescriptorProto__storage_, serviceArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBServiceDescriptorProto), - .fieldOptions = NULL, - }, - { - .name = "extensionArray", - .number = GPBFileDescriptorProto_FieldNumber_ExtensionArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBFileDescriptorProto__storage_, extensionArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBFieldDescriptorProto), - .fieldOptions = NULL, - }, - { - .name = "options", - .number = GPBFileDescriptorProto_FieldNumber_Options, - .hasIndex = 9, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBFileDescriptorProto__storage_, options), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBFileOptions), - .fieldOptions = NULL, - }, - { - .name = "sourceCodeInfo", - .number = GPBFileDescriptorProto_FieldNumber_SourceCodeInfo, - .hasIndex = 10, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBFileDescriptorProto__storage_, sourceCodeInfo), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBSourceCodeInfo), - .fieldOptions = NULL, - }, - { - .name = "publicDependencyArray", - .number = GPBFileDescriptorProto_FieldNumber_PublicDependencyArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBFileDescriptorProto__storage_, publicDependencyArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "weakDependencyArray", - .number = GPBFileDescriptorProto_FieldNumber_WeakDependencyArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBFileDescriptorProto__storage_, weakDependencyArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "syntax", - .number = GPBFileDescriptorProto_FieldNumber_Syntax, - .hasIndex = 11, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFileDescriptorProto__storage_, syntax), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBFileDescriptorProto class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBFileDescriptorProto__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBDescriptorProto - -@implementation GPBDescriptorProto - -@dynamic hasName, name; -@dynamic fieldArray, fieldArray_Count; -@dynamic extensionArray, extensionArray_Count; -@dynamic nestedTypeArray, nestedTypeArray_Count; -@dynamic enumTypeArray, enumTypeArray_Count; -@dynamic extensionRangeArray, extensionRangeArray_Count; -@dynamic oneofDeclArray, oneofDeclArray_Count; -@dynamic hasOptions, options; -@dynamic reservedRangeArray, reservedRangeArray_Count; -@dynamic reservedNameArray, reservedNameArray_Count; - -typedef struct GPBDescriptorProto__storage_ { - uint32_t _has_storage_[1]; - NSString *name; - NSMutableArray *fieldArray; - NSMutableArray *nestedTypeArray; - NSMutableArray *enumTypeArray; - NSMutableArray *extensionRangeArray; - NSMutableArray *extensionArray; - GPBMessageOptions *options; - NSMutableArray *oneofDeclArray; - NSMutableArray *reservedRangeArray; - NSMutableArray *reservedNameArray; -} GPBDescriptorProto__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "name", - .number = GPBDescriptorProto_FieldNumber_Name, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBDescriptorProto__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "fieldArray", - .number = GPBDescriptorProto_FieldNumber_FieldArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBDescriptorProto__storage_, fieldArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBFieldDescriptorProto), - .fieldOptions = NULL, - }, - { - .name = "nestedTypeArray", - .number = GPBDescriptorProto_FieldNumber_NestedTypeArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBDescriptorProto__storage_, nestedTypeArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBDescriptorProto), - .fieldOptions = NULL, - }, - { - .name = "enumTypeArray", - .number = GPBDescriptorProto_FieldNumber_EnumTypeArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBDescriptorProto__storage_, enumTypeArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBEnumDescriptorProto), - .fieldOptions = NULL, - }, - { - .name = "extensionRangeArray", - .number = GPBDescriptorProto_FieldNumber_ExtensionRangeArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBDescriptorProto__storage_, extensionRangeArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBDescriptorProto_ExtensionRange), - .fieldOptions = NULL, - }, - { - .name = "extensionArray", - .number = GPBDescriptorProto_FieldNumber_ExtensionArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBDescriptorProto__storage_, extensionArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBFieldDescriptorProto), - .fieldOptions = NULL, - }, - { - .name = "options", - .number = GPBDescriptorProto_FieldNumber_Options, - .hasIndex = 7, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBDescriptorProto__storage_, options), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBMessageOptions), - .fieldOptions = NULL, - }, - { - .name = "oneofDeclArray", - .number = GPBDescriptorProto_FieldNumber_OneofDeclArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBDescriptorProto__storage_, oneofDeclArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBOneofDescriptorProto), - .fieldOptions = NULL, - }, - { - .name = "reservedRangeArray", - .number = GPBDescriptorProto_FieldNumber_ReservedRangeArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBDescriptorProto__storage_, reservedRangeArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBDescriptorProto_ReservedRange), - .fieldOptions = NULL, - }, - { - .name = "reservedNameArray", - .number = GPBDescriptorProto_FieldNumber_ReservedNameArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBDescriptorProto__storage_, reservedNameArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBDescriptorProto class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBDescriptorProto__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBDescriptorProto_ExtensionRange - -@implementation GPBDescriptorProto_ExtensionRange - -@dynamic hasStart, start; -@dynamic hasEnd, end; - -typedef struct GPBDescriptorProto_ExtensionRange__storage_ { - uint32_t _has_storage_[1]; - int32_t start; - int32_t end; -} GPBDescriptorProto_ExtensionRange__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "start", - .number = GPBDescriptorProto_ExtensionRange_FieldNumber_Start, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBDescriptorProto_ExtensionRange__storage_, start), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "end", - .number = GPBDescriptorProto_ExtensionRange_FieldNumber_End, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBDescriptorProto_ExtensionRange__storage_, end), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBDescriptorProto_ExtensionRange class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBDescriptorProto_ExtensionRange__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBDescriptorProto_ReservedRange - -@implementation GPBDescriptorProto_ReservedRange - -@dynamic hasStart, start; -@dynamic hasEnd, end; - -typedef struct GPBDescriptorProto_ReservedRange__storage_ { - uint32_t _has_storage_[1]; - int32_t start; - int32_t end; -} GPBDescriptorProto_ReservedRange__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "start", - .number = GPBDescriptorProto_ReservedRange_FieldNumber_Start, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBDescriptorProto_ReservedRange__storage_, start), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "end", - .number = GPBDescriptorProto_ReservedRange_FieldNumber_End, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBDescriptorProto_ReservedRange__storage_, end), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBDescriptorProto_ReservedRange class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBDescriptorProto_ReservedRange__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBFieldDescriptorProto - -@implementation GPBFieldDescriptorProto - -@dynamic hasName, name; -@dynamic hasNumber, number; -@dynamic hasLabel, label; -@dynamic hasType, type; -@dynamic hasTypeName, typeName; -@dynamic hasExtendee, extendee; -@dynamic hasDefaultValue, defaultValue; -@dynamic hasOneofIndex, oneofIndex; -@dynamic hasJsonName, jsonName; -@dynamic hasOptions, options; - -typedef struct GPBFieldDescriptorProto__storage_ { - uint32_t _has_storage_[1]; - int32_t number; - GPBFieldDescriptorProto_Label label; - GPBFieldDescriptorProto_Type type; - int32_t oneofIndex; - NSString *name; - NSString *extendee; - NSString *typeName; - NSString *defaultValue; - GPBFieldOptions *options; - NSString *jsonName; -} GPBFieldDescriptorProto__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "name", - .number = GPBFieldDescriptorProto_FieldNumber_Name, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFieldDescriptorProto__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "extendee", - .number = GPBFieldDescriptorProto_FieldNumber_Extendee, - .hasIndex = 5, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFieldDescriptorProto__storage_, extendee), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "number", - .number = GPBFieldDescriptorProto_FieldNumber_Number, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBFieldDescriptorProto__storage_, number), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "label", - .number = GPBFieldDescriptorProto_FieldNumber_Label, - .hasIndex = 2, - .flags = GPBFieldOptional | GPBFieldHasEnumDescriptor, - .dataType = GPBDataTypeEnum, - .offset = offsetof(GPBFieldDescriptorProto__storage_, label), - .defaultValue.valueEnum = GPBFieldDescriptorProto_Label_LabelOptional, - .dataTypeSpecific.enumDescFunc = GPBFieldDescriptorProto_Label_EnumDescriptor, - .fieldOptions = NULL, - }, - { - .name = "type", - .number = GPBFieldDescriptorProto_FieldNumber_Type, - .hasIndex = 3, - .flags = GPBFieldOptional | GPBFieldHasEnumDescriptor, - .dataType = GPBDataTypeEnum, - .offset = offsetof(GPBFieldDescriptorProto__storage_, type), - .defaultValue.valueEnum = GPBFieldDescriptorProto_Type_TypeDouble, - .dataTypeSpecific.enumDescFunc = GPBFieldDescriptorProto_Type_EnumDescriptor, - .fieldOptions = NULL, - }, - { - .name = "typeName", - .number = GPBFieldDescriptorProto_FieldNumber_TypeName, - .hasIndex = 4, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFieldDescriptorProto__storage_, typeName), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "defaultValue", - .number = GPBFieldDescriptorProto_FieldNumber_DefaultValue, - .hasIndex = 6, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFieldDescriptorProto__storage_, defaultValue), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "options", - .number = GPBFieldDescriptorProto_FieldNumber_Options, - .hasIndex = 9, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBFieldDescriptorProto__storage_, options), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBFieldOptions), - .fieldOptions = NULL, - }, - { - .name = "oneofIndex", - .number = GPBFieldDescriptorProto_FieldNumber_OneofIndex, - .hasIndex = 7, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBFieldDescriptorProto__storage_, oneofIndex), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "jsonName", - .number = GPBFieldDescriptorProto_FieldNumber_JsonName, - .hasIndex = 8, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFieldDescriptorProto__storage_, jsonName), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; - static GPBMessageEnumDescription enums[] = { - { .enumDescriptorFunc = GPBFieldDescriptorProto_Type_EnumDescriptor }, - { .enumDescriptorFunc = GPBFieldDescriptorProto_Label_EnumDescriptor }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBFieldDescriptorProto class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:enums - enumCount:sizeof(enums) / sizeof(GPBMessageEnumDescription) - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBFieldDescriptorProto__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - Enum GPBFieldDescriptorProto_Type - -GPBEnumDescriptor *GPBFieldDescriptorProto_Type_EnumDescriptor(void) { - static GPBEnumDescriptor *descriptor = NULL; - if (!descriptor) { - static GPBMessageEnumValueDescription values[] = { - { .name = "TypeDouble", .number = GPBFieldDescriptorProto_Type_TypeDouble }, - { .name = "TypeFloat", .number = GPBFieldDescriptorProto_Type_TypeFloat }, - { .name = "TypeInt64", .number = GPBFieldDescriptorProto_Type_TypeInt64 }, - { .name = "TypeUint64", .number = GPBFieldDescriptorProto_Type_TypeUint64 }, - { .name = "TypeInt32", .number = GPBFieldDescriptorProto_Type_TypeInt32 }, - { .name = "TypeFixed64", .number = GPBFieldDescriptorProto_Type_TypeFixed64 }, - { .name = "TypeFixed32", .number = GPBFieldDescriptorProto_Type_TypeFixed32 }, - { .name = "TypeBool", .number = GPBFieldDescriptorProto_Type_TypeBool }, - { .name = "TypeString", .number = GPBFieldDescriptorProto_Type_TypeString }, - { .name = "TypeGroup", .number = GPBFieldDescriptorProto_Type_TypeGroup }, - { .name = "TypeMessage", .number = GPBFieldDescriptorProto_Type_TypeMessage }, - { .name = "TypeBytes", .number = GPBFieldDescriptorProto_Type_TypeBytes }, - { .name = "TypeUint32", .number = GPBFieldDescriptorProto_Type_TypeUint32 }, - { .name = "TypeEnum", .number = GPBFieldDescriptorProto_Type_TypeEnum }, - { .name = "TypeSfixed32", .number = GPBFieldDescriptorProto_Type_TypeSfixed32 }, - { .name = "TypeSfixed64", .number = GPBFieldDescriptorProto_Type_TypeSfixed64 }, - { .name = "TypeSint32", .number = GPBFieldDescriptorProto_Type_TypeSint32 }, - { .name = "TypeSint64", .number = GPBFieldDescriptorProto_Type_TypeSint64 }, - }; - descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBFieldDescriptorProto_Type) - values:values - valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription) - enumVerifier:GPBFieldDescriptorProto_Type_IsValidValue]; - } - return descriptor; -} - -BOOL GPBFieldDescriptorProto_Type_IsValidValue(int32_t value__) { - switch (value__) { - case GPBFieldDescriptorProto_Type_TypeDouble: - case GPBFieldDescriptorProto_Type_TypeFloat: - case GPBFieldDescriptorProto_Type_TypeInt64: - case GPBFieldDescriptorProto_Type_TypeUint64: - case GPBFieldDescriptorProto_Type_TypeInt32: - case GPBFieldDescriptorProto_Type_TypeFixed64: - case GPBFieldDescriptorProto_Type_TypeFixed32: - case GPBFieldDescriptorProto_Type_TypeBool: - case GPBFieldDescriptorProto_Type_TypeString: - case GPBFieldDescriptorProto_Type_TypeGroup: - case GPBFieldDescriptorProto_Type_TypeMessage: - case GPBFieldDescriptorProto_Type_TypeBytes: - case GPBFieldDescriptorProto_Type_TypeUint32: - case GPBFieldDescriptorProto_Type_TypeEnum: - case GPBFieldDescriptorProto_Type_TypeSfixed32: - case GPBFieldDescriptorProto_Type_TypeSfixed64: - case GPBFieldDescriptorProto_Type_TypeSint32: - case GPBFieldDescriptorProto_Type_TypeSint64: - return YES; - default: - return NO; - } -} - -#pragma mark - Enum GPBFieldDescriptorProto_Label - -GPBEnumDescriptor *GPBFieldDescriptorProto_Label_EnumDescriptor(void) { - static GPBEnumDescriptor *descriptor = NULL; - if (!descriptor) { - static GPBMessageEnumValueDescription values[] = { - { .name = "LabelOptional", .number = GPBFieldDescriptorProto_Label_LabelOptional }, - { .name = "LabelRequired", .number = GPBFieldDescriptorProto_Label_LabelRequired }, - { .name = "LabelRepeated", .number = GPBFieldDescriptorProto_Label_LabelRepeated }, - }; - descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBFieldDescriptorProto_Label) - values:values - valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription) - enumVerifier:GPBFieldDescriptorProto_Label_IsValidValue]; - } - return descriptor; -} - -BOOL GPBFieldDescriptorProto_Label_IsValidValue(int32_t value__) { - switch (value__) { - case GPBFieldDescriptorProto_Label_LabelOptional: - case GPBFieldDescriptorProto_Label_LabelRequired: - case GPBFieldDescriptorProto_Label_LabelRepeated: - return YES; - default: - return NO; - } -} - -#pragma mark - GPBOneofDescriptorProto - -@implementation GPBOneofDescriptorProto - -@dynamic hasName, name; - -typedef struct GPBOneofDescriptorProto__storage_ { - uint32_t _has_storage_[1]; - NSString *name; -} GPBOneofDescriptorProto__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "name", - .number = GPBOneofDescriptorProto_FieldNumber_Name, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBOneofDescriptorProto__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBOneofDescriptorProto class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBOneofDescriptorProto__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBEnumDescriptorProto - -@implementation GPBEnumDescriptorProto - -@dynamic hasName, name; -@dynamic valueArray, valueArray_Count; -@dynamic hasOptions, options; - -typedef struct GPBEnumDescriptorProto__storage_ { - uint32_t _has_storage_[1]; - NSString *name; - NSMutableArray *valueArray; - GPBEnumOptions *options; -} GPBEnumDescriptorProto__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "name", - .number = GPBEnumDescriptorProto_FieldNumber_Name, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBEnumDescriptorProto__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "valueArray", - .number = GPBEnumDescriptorProto_FieldNumber_ValueArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBEnumDescriptorProto__storage_, valueArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBEnumValueDescriptorProto), - .fieldOptions = NULL, - }, - { - .name = "options", - .number = GPBEnumDescriptorProto_FieldNumber_Options, - .hasIndex = 2, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBEnumDescriptorProto__storage_, options), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBEnumOptions), - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBEnumDescriptorProto class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBEnumDescriptorProto__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBEnumValueDescriptorProto - -@implementation GPBEnumValueDescriptorProto - -@dynamic hasName, name; -@dynamic hasNumber, number; -@dynamic hasOptions, options; - -typedef struct GPBEnumValueDescriptorProto__storage_ { - uint32_t _has_storage_[1]; - int32_t number; - NSString *name; - GPBEnumValueOptions *options; -} GPBEnumValueDescriptorProto__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "name", - .number = GPBEnumValueDescriptorProto_FieldNumber_Name, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBEnumValueDescriptorProto__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "number", - .number = GPBEnumValueDescriptorProto_FieldNumber_Number, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBEnumValueDescriptorProto__storage_, number), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "options", - .number = GPBEnumValueDescriptorProto_FieldNumber_Options, - .hasIndex = 2, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBEnumValueDescriptorProto__storage_, options), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBEnumValueOptions), - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBEnumValueDescriptorProto class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBEnumValueDescriptorProto__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBServiceDescriptorProto - -@implementation GPBServiceDescriptorProto - -@dynamic hasName, name; -@dynamic methodArray, methodArray_Count; -@dynamic hasOptions, options; - -typedef struct GPBServiceDescriptorProto__storage_ { - uint32_t _has_storage_[1]; - NSString *name; - NSMutableArray *methodArray; - GPBServiceOptions *options; -} GPBServiceDescriptorProto__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "name", - .number = GPBServiceDescriptorProto_FieldNumber_Name, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBServiceDescriptorProto__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "methodArray", - .number = GPBServiceDescriptorProto_FieldNumber_MethodArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBServiceDescriptorProto__storage_, methodArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBMethodDescriptorProto), - .fieldOptions = NULL, - }, - { - .name = "options", - .number = GPBServiceDescriptorProto_FieldNumber_Options, - .hasIndex = 2, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBServiceDescriptorProto__storage_, options), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBServiceOptions), - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBServiceDescriptorProto class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBServiceDescriptorProto__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBMethodDescriptorProto - -@implementation GPBMethodDescriptorProto - -@dynamic hasName, name; -@dynamic hasInputType, inputType; -@dynamic hasOutputType, outputType; -@dynamic hasOptions, options; -@dynamic hasClientStreaming, clientStreaming; -@dynamic hasServerStreaming, serverStreaming; - -typedef struct GPBMethodDescriptorProto__storage_ { - uint32_t _has_storage_[1]; - BOOL clientStreaming; - BOOL serverStreaming; - NSString *name; - NSString *inputType; - NSString *outputType; - GPBMethodOptions *options; -} GPBMethodDescriptorProto__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "name", - .number = GPBMethodDescriptorProto_FieldNumber_Name, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBMethodDescriptorProto__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "inputType", - .number = GPBMethodDescriptorProto_FieldNumber_InputType, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBMethodDescriptorProto__storage_, inputType), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "outputType", - .number = GPBMethodDescriptorProto_FieldNumber_OutputType, - .hasIndex = 2, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBMethodDescriptorProto__storage_, outputType), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "options", - .number = GPBMethodDescriptorProto_FieldNumber_Options, - .hasIndex = 3, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBMethodDescriptorProto__storage_, options), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBMethodOptions), - .fieldOptions = NULL, - }, - { - .name = "clientStreaming", - .number = GPBMethodDescriptorProto_FieldNumber_ClientStreaming, - .hasIndex = 4, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBMethodDescriptorProto__storage_, clientStreaming), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "serverStreaming", - .number = GPBMethodDescriptorProto_FieldNumber_ServerStreaming, - .hasIndex = 5, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBMethodDescriptorProto__storage_, serverStreaming), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBMethodDescriptorProto class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBMethodDescriptorProto__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBFileOptions - -@implementation GPBFileOptions - -@dynamic hasJavaPackage, javaPackage; -@dynamic hasJavaOuterClassname, javaOuterClassname; -@dynamic hasJavaMultipleFiles, javaMultipleFiles; -@dynamic hasJavaGenerateEqualsAndHash, javaGenerateEqualsAndHash; -@dynamic hasJavaStringCheckUtf8, javaStringCheckUtf8; -@dynamic hasOptimizeFor, optimizeFor; -@dynamic hasGoPackage, goPackage; -@dynamic hasCcGenericServices, ccGenericServices; -@dynamic hasJavaGenericServices, javaGenericServices; -@dynamic hasPyGenericServices, pyGenericServices; -@dynamic hasDeprecated, deprecated; -@dynamic hasCcEnableArenas, ccEnableArenas; -@dynamic hasObjcClassPrefix, objcClassPrefix; -@dynamic hasCsharpNamespace, csharpNamespace; -@dynamic hasJavananoUseDeprecatedPackage, javananoUseDeprecatedPackage; -@dynamic uninterpretedOptionArray, uninterpretedOptionArray_Count; - -typedef struct GPBFileOptions__storage_ { - uint32_t _has_storage_[1]; - BOOL javaMultipleFiles; - BOOL ccGenericServices; - BOOL javaGenericServices; - BOOL pyGenericServices; - BOOL javaGenerateEqualsAndHash; - BOOL deprecated; - BOOL javaStringCheckUtf8; - BOOL ccEnableArenas; - BOOL javananoUseDeprecatedPackage; - GPBFileOptions_OptimizeMode optimizeFor; - NSString *javaPackage; - NSString *javaOuterClassname; - NSString *goPackage; - NSString *objcClassPrefix; - NSString *csharpNamespace; - NSMutableArray *uninterpretedOptionArray; -} GPBFileOptions__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "javaPackage", - .number = GPBFileOptions_FieldNumber_JavaPackage, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFileOptions__storage_, javaPackage), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "javaOuterClassname", - .number = GPBFileOptions_FieldNumber_JavaOuterClassname, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFileOptions__storage_, javaOuterClassname), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "optimizeFor", - .number = GPBFileOptions_FieldNumber_OptimizeFor, - .hasIndex = 5, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue | GPBFieldHasEnumDescriptor, - .dataType = GPBDataTypeEnum, - .offset = offsetof(GPBFileOptions__storage_, optimizeFor), - .defaultValue.valueEnum = GPBFileOptions_OptimizeMode_Speed, - .dataTypeSpecific.enumDescFunc = GPBFileOptions_OptimizeMode_EnumDescriptor, - .fieldOptions = NULL, - }, - { - .name = "javaMultipleFiles", - .number = GPBFileOptions_FieldNumber_JavaMultipleFiles, - .hasIndex = 2, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBFileOptions__storage_, javaMultipleFiles), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "goPackage", - .number = GPBFileOptions_FieldNumber_GoPackage, - .hasIndex = 6, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFileOptions__storage_, goPackage), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "ccGenericServices", - .number = GPBFileOptions_FieldNumber_CcGenericServices, - .hasIndex = 7, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBFileOptions__storage_, ccGenericServices), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "javaGenericServices", - .number = GPBFileOptions_FieldNumber_JavaGenericServices, - .hasIndex = 8, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBFileOptions__storage_, javaGenericServices), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "pyGenericServices", - .number = GPBFileOptions_FieldNumber_PyGenericServices, - .hasIndex = 9, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBFileOptions__storage_, pyGenericServices), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "javaGenerateEqualsAndHash", - .number = GPBFileOptions_FieldNumber_JavaGenerateEqualsAndHash, - .hasIndex = 3, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBFileOptions__storage_, javaGenerateEqualsAndHash), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "deprecated", - .number = GPBFileOptions_FieldNumber_Deprecated, - .hasIndex = 10, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBFileOptions__storage_, deprecated), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "javaStringCheckUtf8", - .number = GPBFileOptions_FieldNumber_JavaStringCheckUtf8, - .hasIndex = 4, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBFileOptions__storage_, javaStringCheckUtf8), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "ccEnableArenas", - .number = GPBFileOptions_FieldNumber_CcEnableArenas, - .hasIndex = 11, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBFileOptions__storage_, ccEnableArenas), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "objcClassPrefix", - .number = GPBFileOptions_FieldNumber_ObjcClassPrefix, - .hasIndex = 12, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFileOptions__storage_, objcClassPrefix), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "csharpNamespace", - .number = GPBFileOptions_FieldNumber_CsharpNamespace, - .hasIndex = 13, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBFileOptions__storage_, csharpNamespace), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "javananoUseDeprecatedPackage", - .number = GPBFileOptions_FieldNumber_JavananoUseDeprecatedPackage, - .hasIndex = 14, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBFileOptions__storage_, javananoUseDeprecatedPackage), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - #if GPBOBJC_INCLUDE_FIELD_OPTIONS - .fieldOptions = "\000\000\000\002\030\001", - #else - .fieldOptions = NULL, - #endif // GPBOBJC_INCLUDE_FIELD_OPTIONS - }, - { - .name = "uninterpretedOptionArray", - .number = GPBFileOptions_FieldNumber_UninterpretedOptionArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBFileOptions__storage_, uninterpretedOptionArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBUninterpretedOption), - .fieldOptions = NULL, - }, - }; - static GPBMessageEnumDescription enums[] = { - { .enumDescriptorFunc = GPBFileOptions_OptimizeMode_EnumDescriptor }, - }; - static GPBExtensionRange ranges[] = { - { .start = 1000, .end = 536870912 }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBFileOptions class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:enums - enumCount:sizeof(enums) / sizeof(GPBMessageEnumDescription) - ranges:ranges - rangeCount:sizeof(ranges) / sizeof(GPBExtensionRange) - storageSize:sizeof(GPBFileOptions__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - Enum GPBFileOptions_OptimizeMode - -GPBEnumDescriptor *GPBFileOptions_OptimizeMode_EnumDescriptor(void) { - static GPBEnumDescriptor *descriptor = NULL; - if (!descriptor) { - static GPBMessageEnumValueDescription values[] = { - { .name = "Speed", .number = GPBFileOptions_OptimizeMode_Speed }, - { .name = "CodeSize", .number = GPBFileOptions_OptimizeMode_CodeSize }, - { .name = "LiteRuntime", .number = GPBFileOptions_OptimizeMode_LiteRuntime }, - }; - descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBFileOptions_OptimizeMode) - values:values - valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription) - enumVerifier:GPBFileOptions_OptimizeMode_IsValidValue]; - } - return descriptor; -} - -BOOL GPBFileOptions_OptimizeMode_IsValidValue(int32_t value__) { - switch (value__) { - case GPBFileOptions_OptimizeMode_Speed: - case GPBFileOptions_OptimizeMode_CodeSize: - case GPBFileOptions_OptimizeMode_LiteRuntime: - return YES; - default: - return NO; - } -} - -#pragma mark - GPBMessageOptions - -@implementation GPBMessageOptions - -@dynamic hasMessageSetWireFormat, messageSetWireFormat; -@dynamic hasNoStandardDescriptorAccessor, noStandardDescriptorAccessor; -@dynamic hasDeprecated, deprecated; -@dynamic hasMapEntry, mapEntry; -@dynamic uninterpretedOptionArray, uninterpretedOptionArray_Count; - -typedef struct GPBMessageOptions__storage_ { - uint32_t _has_storage_[1]; - BOOL messageSetWireFormat; - BOOL noStandardDescriptorAccessor; - BOOL deprecated; - BOOL mapEntry; - NSMutableArray *uninterpretedOptionArray; -} GPBMessageOptions__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "messageSetWireFormat", - .number = GPBMessageOptions_FieldNumber_MessageSetWireFormat, - .hasIndex = 0, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBMessageOptions__storage_, messageSetWireFormat), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "noStandardDescriptorAccessor", - .number = GPBMessageOptions_FieldNumber_NoStandardDescriptorAccessor, - .hasIndex = 1, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBMessageOptions__storage_, noStandardDescriptorAccessor), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "deprecated", - .number = GPBMessageOptions_FieldNumber_Deprecated, - .hasIndex = 2, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBMessageOptions__storage_, deprecated), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "mapEntry", - .number = GPBMessageOptions_FieldNumber_MapEntry, - .hasIndex = 3, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBMessageOptions__storage_, mapEntry), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "uninterpretedOptionArray", - .number = GPBMessageOptions_FieldNumber_UninterpretedOptionArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBMessageOptions__storage_, uninterpretedOptionArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBUninterpretedOption), - .fieldOptions = NULL, - }, - }; - static GPBExtensionRange ranges[] = { - { .start = 1000, .end = 536870912 }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBMessageOptions class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:ranges - rangeCount:sizeof(ranges) / sizeof(GPBExtensionRange) - storageSize:sizeof(GPBMessageOptions__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBFieldOptions - -@implementation GPBFieldOptions - -@dynamic hasCtype, ctype; -@dynamic hasPacked, packed; -@dynamic hasJstype, jstype; -@dynamic hasLazy, lazy; -@dynamic hasDeprecated, deprecated; -@dynamic hasWeak, weak; -@dynamic uninterpretedOptionArray, uninterpretedOptionArray_Count; - -typedef struct GPBFieldOptions__storage_ { - uint32_t _has_storage_[1]; - BOOL packed; - BOOL deprecated; - BOOL lazy; - BOOL weak; - GPBFieldOptions_CType ctype; - GPBFieldOptions_JSType jstype; - NSMutableArray *uninterpretedOptionArray; -} GPBFieldOptions__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "ctype", - .number = GPBFieldOptions_FieldNumber_Ctype, - .hasIndex = 0, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue | GPBFieldHasEnumDescriptor, - .dataType = GPBDataTypeEnum, - .offset = offsetof(GPBFieldOptions__storage_, ctype), - .defaultValue.valueEnum = GPBFieldOptions_CType_String, - .dataTypeSpecific.enumDescFunc = GPBFieldOptions_CType_EnumDescriptor, - .fieldOptions = NULL, - }, - { - .name = "packed", - .number = GPBFieldOptions_FieldNumber_Packed, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBFieldOptions__storage_, packed), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "deprecated", - .number = GPBFieldOptions_FieldNumber_Deprecated, - .hasIndex = 4, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBFieldOptions__storage_, deprecated), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "lazy", - .number = GPBFieldOptions_FieldNumber_Lazy, - .hasIndex = 3, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBFieldOptions__storage_, lazy), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "jstype", - .number = GPBFieldOptions_FieldNumber_Jstype, - .hasIndex = 2, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue | GPBFieldHasEnumDescriptor, - .dataType = GPBDataTypeEnum, - .offset = offsetof(GPBFieldOptions__storage_, jstype), - .defaultValue.valueEnum = GPBFieldOptions_JSType_JsNormal, - .dataTypeSpecific.enumDescFunc = GPBFieldOptions_JSType_EnumDescriptor, - .fieldOptions = NULL, - }, - { - .name = "weak", - .number = GPBFieldOptions_FieldNumber_Weak, - .hasIndex = 5, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBFieldOptions__storage_, weak), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "uninterpretedOptionArray", - .number = GPBFieldOptions_FieldNumber_UninterpretedOptionArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBFieldOptions__storage_, uninterpretedOptionArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBUninterpretedOption), - .fieldOptions = NULL, - }, - }; - static GPBMessageEnumDescription enums[] = { - { .enumDescriptorFunc = GPBFieldOptions_CType_EnumDescriptor }, - { .enumDescriptorFunc = GPBFieldOptions_JSType_EnumDescriptor }, - }; - static GPBExtensionRange ranges[] = { - { .start = 1000, .end = 536870912 }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBFieldOptions class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:enums - enumCount:sizeof(enums) / sizeof(GPBMessageEnumDescription) - ranges:ranges - rangeCount:sizeof(ranges) / sizeof(GPBExtensionRange) - storageSize:sizeof(GPBFieldOptions__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - Enum GPBFieldOptions_CType - -GPBEnumDescriptor *GPBFieldOptions_CType_EnumDescriptor(void) { - static GPBEnumDescriptor *descriptor = NULL; - if (!descriptor) { - static GPBMessageEnumValueDescription values[] = { - { .name = "String", .number = GPBFieldOptions_CType_String }, - { .name = "Cord", .number = GPBFieldOptions_CType_Cord }, - { .name = "StringPiece", .number = GPBFieldOptions_CType_StringPiece }, - }; - descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBFieldOptions_CType) - values:values - valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription) - enumVerifier:GPBFieldOptions_CType_IsValidValue]; - } - return descriptor; -} - -BOOL GPBFieldOptions_CType_IsValidValue(int32_t value__) { - switch (value__) { - case GPBFieldOptions_CType_String: - case GPBFieldOptions_CType_Cord: - case GPBFieldOptions_CType_StringPiece: - return YES; - default: - return NO; - } -} - -#pragma mark - Enum GPBFieldOptions_JSType - -GPBEnumDescriptor *GPBFieldOptions_JSType_EnumDescriptor(void) { - static GPBEnumDescriptor *descriptor = NULL; - if (!descriptor) { - static GPBMessageEnumValueDescription values[] = { - { .name = "JsNormal", .number = GPBFieldOptions_JSType_JsNormal }, - { .name = "JsString", .number = GPBFieldOptions_JSType_JsString }, - { .name = "JsNumber", .number = GPBFieldOptions_JSType_JsNumber }, - }; - descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBFieldOptions_JSType) - values:values - valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription) - enumVerifier:GPBFieldOptions_JSType_IsValidValue]; - } - return descriptor; -} - -BOOL GPBFieldOptions_JSType_IsValidValue(int32_t value__) { - switch (value__) { - case GPBFieldOptions_JSType_JsNormal: - case GPBFieldOptions_JSType_JsString: - case GPBFieldOptions_JSType_JsNumber: - return YES; - default: - return NO; - } -} - -#pragma mark - GPBEnumOptions - -@implementation GPBEnumOptions - -@dynamic hasAllowAlias, allowAlias; -@dynamic hasDeprecated, deprecated; -@dynamic uninterpretedOptionArray, uninterpretedOptionArray_Count; - -typedef struct GPBEnumOptions__storage_ { - uint32_t _has_storage_[1]; - BOOL allowAlias; - BOOL deprecated; - NSMutableArray *uninterpretedOptionArray; -} GPBEnumOptions__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "allowAlias", - .number = GPBEnumOptions_FieldNumber_AllowAlias, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBEnumOptions__storage_, allowAlias), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "deprecated", - .number = GPBEnumOptions_FieldNumber_Deprecated, - .hasIndex = 1, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBEnumOptions__storage_, deprecated), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "uninterpretedOptionArray", - .number = GPBEnumOptions_FieldNumber_UninterpretedOptionArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBEnumOptions__storage_, uninterpretedOptionArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBUninterpretedOption), - .fieldOptions = NULL, - }, - }; - static GPBExtensionRange ranges[] = { - { .start = 1000, .end = 536870912 }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBEnumOptions class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:ranges - rangeCount:sizeof(ranges) / sizeof(GPBExtensionRange) - storageSize:sizeof(GPBEnumOptions__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBEnumValueOptions - -@implementation GPBEnumValueOptions - -@dynamic hasDeprecated, deprecated; -@dynamic uninterpretedOptionArray, uninterpretedOptionArray_Count; - -typedef struct GPBEnumValueOptions__storage_ { - uint32_t _has_storage_[1]; - BOOL deprecated; - NSMutableArray *uninterpretedOptionArray; -} GPBEnumValueOptions__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "deprecated", - .number = GPBEnumValueOptions_FieldNumber_Deprecated, - .hasIndex = 0, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBEnumValueOptions__storage_, deprecated), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "uninterpretedOptionArray", - .number = GPBEnumValueOptions_FieldNumber_UninterpretedOptionArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBEnumValueOptions__storage_, uninterpretedOptionArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBUninterpretedOption), - .fieldOptions = NULL, - }, - }; - static GPBExtensionRange ranges[] = { - { .start = 1000, .end = 536870912 }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBEnumValueOptions class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:ranges - rangeCount:sizeof(ranges) / sizeof(GPBExtensionRange) - storageSize:sizeof(GPBEnumValueOptions__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBServiceOptions - -@implementation GPBServiceOptions - -@dynamic hasDeprecated, deprecated; -@dynamic uninterpretedOptionArray, uninterpretedOptionArray_Count; - -typedef struct GPBServiceOptions__storage_ { - uint32_t _has_storage_[1]; - BOOL deprecated; - NSMutableArray *uninterpretedOptionArray; -} GPBServiceOptions__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "deprecated", - .number = GPBServiceOptions_FieldNumber_Deprecated, - .hasIndex = 0, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBServiceOptions__storage_, deprecated), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "uninterpretedOptionArray", - .number = GPBServiceOptions_FieldNumber_UninterpretedOptionArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBServiceOptions__storage_, uninterpretedOptionArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBUninterpretedOption), - .fieldOptions = NULL, - }, - }; - static GPBExtensionRange ranges[] = { - { .start = 1000, .end = 536870912 }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBServiceOptions class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:ranges - rangeCount:sizeof(ranges) / sizeof(GPBExtensionRange) - storageSize:sizeof(GPBServiceOptions__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBMethodOptions - -@implementation GPBMethodOptions - -@dynamic hasDeprecated, deprecated; -@dynamic uninterpretedOptionArray, uninterpretedOptionArray_Count; - -typedef struct GPBMethodOptions__storage_ { - uint32_t _has_storage_[1]; - BOOL deprecated; - NSMutableArray *uninterpretedOptionArray; -} GPBMethodOptions__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "deprecated", - .number = GPBMethodOptions_FieldNumber_Deprecated, - .hasIndex = 0, - .flags = GPBFieldOptional | GPBFieldHasDefaultValue, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBMethodOptions__storage_, deprecated), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "uninterpretedOptionArray", - .number = GPBMethodOptions_FieldNumber_UninterpretedOptionArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBMethodOptions__storage_, uninterpretedOptionArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBUninterpretedOption), - .fieldOptions = NULL, - }, - }; - static GPBExtensionRange ranges[] = { - { .start = 1000, .end = 536870912 }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBMethodOptions class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:ranges - rangeCount:sizeof(ranges) / sizeof(GPBExtensionRange) - storageSize:sizeof(GPBMethodOptions__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBUninterpretedOption - -@implementation GPBUninterpretedOption - -@dynamic nameArray, nameArray_Count; -@dynamic hasIdentifierValue, identifierValue; -@dynamic hasPositiveIntValue, positiveIntValue; -@dynamic hasNegativeIntValue, negativeIntValue; -@dynamic hasDoubleValue, doubleValue; -@dynamic hasStringValue, stringValue; -@dynamic hasAggregateValue, aggregateValue; - -typedef struct GPBUninterpretedOption__storage_ { - uint32_t _has_storage_[1]; - NSMutableArray *nameArray; - NSString *identifierValue; - NSData *stringValue; - NSString *aggregateValue; - uint64_t positiveIntValue; - int64_t negativeIntValue; - double doubleValue; -} GPBUninterpretedOption__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "nameArray", - .number = GPBUninterpretedOption_FieldNumber_NameArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBUninterpretedOption__storage_, nameArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBUninterpretedOption_NamePart), - .fieldOptions = NULL, - }, - { - .name = "identifierValue", - .number = GPBUninterpretedOption_FieldNumber_IdentifierValue, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBUninterpretedOption__storage_, identifierValue), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "positiveIntValue", - .number = GPBUninterpretedOption_FieldNumber_PositiveIntValue, - .hasIndex = 2, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeUInt64, - .offset = offsetof(GPBUninterpretedOption__storage_, positiveIntValue), - .defaultValue.valueUInt64 = 0ULL, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "negativeIntValue", - .number = GPBUninterpretedOption_FieldNumber_NegativeIntValue, - .hasIndex = 3, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt64, - .offset = offsetof(GPBUninterpretedOption__storage_, negativeIntValue), - .defaultValue.valueInt64 = 0LL, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "doubleValue", - .number = GPBUninterpretedOption_FieldNumber_DoubleValue, - .hasIndex = 4, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeDouble, - .offset = offsetof(GPBUninterpretedOption__storage_, doubleValue), - .defaultValue.valueDouble = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "stringValue", - .number = GPBUninterpretedOption_FieldNumber_StringValue, - .hasIndex = 5, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeBytes, - .offset = offsetof(GPBUninterpretedOption__storage_, stringValue), - .defaultValue.valueData = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "aggregateValue", - .number = GPBUninterpretedOption_FieldNumber_AggregateValue, - .hasIndex = 6, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBUninterpretedOption__storage_, aggregateValue), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBUninterpretedOption class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBUninterpretedOption__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBUninterpretedOption_NamePart - -@implementation GPBUninterpretedOption_NamePart - -@dynamic hasNamePart, namePart; -@dynamic hasIsExtension, isExtension; - -typedef struct GPBUninterpretedOption_NamePart__storage_ { - uint32_t _has_storage_[1]; - BOOL isExtension; - NSString *namePart; -} GPBUninterpretedOption_NamePart__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "namePart", - .number = GPBUninterpretedOption_NamePart_FieldNumber_NamePart, - .hasIndex = 0, - .flags = GPBFieldRequired, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBUninterpretedOption_NamePart__storage_, namePart), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "isExtension", - .number = GPBUninterpretedOption_NamePart_FieldNumber_IsExtension, - .hasIndex = 1, - .flags = GPBFieldRequired, - .dataType = GPBDataTypeBool, - .offset = offsetof(GPBUninterpretedOption_NamePart__storage_, isExtension), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBUninterpretedOption_NamePart class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBUninterpretedOption_NamePart__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBSourceCodeInfo - -@implementation GPBSourceCodeInfo - -@dynamic locationArray, locationArray_Count; - -typedef struct GPBSourceCodeInfo__storage_ { - uint32_t _has_storage_[1]; - NSMutableArray *locationArray; -} GPBSourceCodeInfo__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "locationArray", - .number = GPBSourceCodeInfo_FieldNumber_LocationArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBSourceCodeInfo__storage_, locationArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBSourceCodeInfo_Location), - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBSourceCodeInfo class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBSourceCodeInfo__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBSourceCodeInfo_Location - -@implementation GPBSourceCodeInfo_Location - -@dynamic pathArray, pathArray_Count; -@dynamic spanArray, spanArray_Count; -@dynamic hasLeadingComments, leadingComments; -@dynamic hasTrailingComments, trailingComments; -@dynamic leadingDetachedCommentsArray, leadingDetachedCommentsArray_Count; - -typedef struct GPBSourceCodeInfo_Location__storage_ { - uint32_t _has_storage_[1]; - GPBInt32Array *pathArray; - GPBInt32Array *spanArray; - NSString *leadingComments; - NSString *trailingComments; - NSMutableArray *leadingDetachedCommentsArray; -} GPBSourceCodeInfo_Location__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "pathArray", - .number = GPBSourceCodeInfo_Location_FieldNumber_PathArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated | GPBFieldPacked, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBSourceCodeInfo_Location__storage_, pathArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = NULL, - #if GPBOBJC_INCLUDE_FIELD_OPTIONS - .fieldOptions = "\000\000\000\002\020\001", - #else - .fieldOptions = NULL, - #endif // GPBOBJC_INCLUDE_FIELD_OPTIONS - }, - { - .name = "spanArray", - .number = GPBSourceCodeInfo_Location_FieldNumber_SpanArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated | GPBFieldPacked, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBSourceCodeInfo_Location__storage_, spanArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = NULL, - #if GPBOBJC_INCLUDE_FIELD_OPTIONS - .fieldOptions = "\000\000\000\002\020\001", - #else - .fieldOptions = NULL, - #endif // GPBOBJC_INCLUDE_FIELD_OPTIONS - }, - { - .name = "leadingComments", - .number = GPBSourceCodeInfo_Location_FieldNumber_LeadingComments, - .hasIndex = 2, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBSourceCodeInfo_Location__storage_, leadingComments), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "trailingComments", - .number = GPBSourceCodeInfo_Location_FieldNumber_TrailingComments, - .hasIndex = 3, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBSourceCodeInfo_Location__storage_, trailingComments), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "leadingDetachedCommentsArray", - .number = GPBSourceCodeInfo_Location_FieldNumber_LeadingDetachedCommentsArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBSourceCodeInfo_Location__storage_, leadingDetachedCommentsArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBSourceCodeInfo_Location class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBSourceCodeInfo_Location__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBGeneratedCodeInfo - -@implementation GPBGeneratedCodeInfo - -@dynamic annotationArray, annotationArray_Count; - -typedef struct GPBGeneratedCodeInfo__storage_ { - uint32_t _has_storage_[1]; - NSMutableArray *annotationArray; -} GPBGeneratedCodeInfo__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "annotationArray", - .number = GPBGeneratedCodeInfo_FieldNumber_AnnotationArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBGeneratedCodeInfo__storage_, annotationArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBGeneratedCodeInfo_Annotation), - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBGeneratedCodeInfo class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBGeneratedCodeInfo__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - GPBGeneratedCodeInfo_Annotation - -@implementation GPBGeneratedCodeInfo_Annotation - -@dynamic pathArray, pathArray_Count; -@dynamic hasSourceFile, sourceFile; -@dynamic hasBegin, begin; -@dynamic hasEnd, end; - -typedef struct GPBGeneratedCodeInfo_Annotation__storage_ { - uint32_t _has_storage_[1]; - int32_t begin; - int32_t end; - GPBInt32Array *pathArray; - NSString *sourceFile; -} GPBGeneratedCodeInfo_Annotation__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "pathArray", - .number = GPBGeneratedCodeInfo_Annotation_FieldNumber_PathArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated | GPBFieldPacked, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBGeneratedCodeInfo_Annotation__storage_, pathArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = NULL, - #if GPBOBJC_INCLUDE_FIELD_OPTIONS - .fieldOptions = "\000\000\000\002\020\001", - #else - .fieldOptions = NULL, - #endif // GPBOBJC_INCLUDE_FIELD_OPTIONS - }, - { - .name = "sourceFile", - .number = GPBGeneratedCodeInfo_Annotation_FieldNumber_SourceFile, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(GPBGeneratedCodeInfo_Annotation__storage_, sourceFile), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "begin", - .number = GPBGeneratedCodeInfo_Annotation_FieldNumber_Begin, - .hasIndex = 2, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBGeneratedCodeInfo_Annotation__storage_, begin), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "end", - .number = GPBGeneratedCodeInfo_Annotation_FieldNumber_End, - .hasIndex = 3, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBGeneratedCodeInfo_Annotation__storage_, end), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[GPBGeneratedCodeInfo_Annotation class] - rootClass:[GPBDescriptorRoot class] - file:GPBDescriptorRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(GPBGeneratedCodeInfo_Annotation__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - - -// @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Duration.pbobjc.h b/objectivec/google/protobuf/Duration.pbobjc.h index ebf9119e3..29888d6cf 100644 --- a/objectivec/google/protobuf/Duration.pbobjc.h +++ b/objectivec/google/protobuf/Duration.pbobjc.h @@ -3,12 +3,15 @@ #import "GPBProtocolBuffers.h" -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 +#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001 #error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. #endif // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CF_EXTERN_C_BEGIN NS_ASSUME_NONNULL_BEGIN @@ -93,4 +96,6 @@ NS_ASSUME_NONNULL_END CF_EXTERN_C_END +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Duration.pbobjc.m b/objectivec/google/protobuf/Duration.pbobjc.m index e4fd4951c..7dd6b64a7 100644 --- a/objectivec/google/protobuf/Duration.pbobjc.m +++ b/objectivec/google/protobuf/Duration.pbobjc.m @@ -5,6 +5,9 @@ #import "google/protobuf/Duration.pbobjc.h" // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma mark - GPBDurationRoot @implementation GPBDurationRoot @@ -46,25 +49,21 @@ typedef struct GPBDuration__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "seconds", + .dataTypeSpecific.className = NULL, .number = GPBDuration_FieldNumber_Seconds, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBDuration__storage_, seconds), .flags = GPBFieldOptional, .dataType = GPBDataTypeInt64, - .offset = offsetof(GPBDuration__storage_, seconds), - .defaultValue.valueInt64 = 0LL, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "nanos", + .dataTypeSpecific.className = NULL, .number = GPBDuration_FieldNumber_Nanos, .hasIndex = 1, + .offset = (uint32_t)offsetof(GPBDuration__storage_, nanos), .flags = GPBFieldOptional, .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBDuration__storage_, nanos), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -72,15 +71,9 @@ typedef struct GPBDuration__storage_ { rootClass:[GPBDurationRoot class] file:GPBDurationRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBDuration__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -90,4 +83,6 @@ typedef struct GPBDuration__storage_ { @end +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Empty.pbobjc.h b/objectivec/google/protobuf/Empty.pbobjc.h index ca09f71db..f33db0175 100644 --- a/objectivec/google/protobuf/Empty.pbobjc.h +++ b/objectivec/google/protobuf/Empty.pbobjc.h @@ -3,12 +3,15 @@ #import "GPBProtocolBuffers.h" -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 +#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001 #error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. #endif // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CF_EXTERN_C_BEGIN NS_ASSUME_NONNULL_BEGIN @@ -45,4 +48,6 @@ NS_ASSUME_NONNULL_END CF_EXTERN_C_END +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Empty.pbobjc.m b/objectivec/google/protobuf/Empty.pbobjc.m index 17f0c1ac6..88753aaf4 100644 --- a/objectivec/google/protobuf/Empty.pbobjc.m +++ b/objectivec/google/protobuf/Empty.pbobjc.m @@ -5,6 +5,9 @@ #import "google/protobuf/Empty.pbobjc.h" // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma mark - GPBEmptyRoot @implementation GPBEmptyRoot @@ -31,7 +34,7 @@ static GPBFileDescriptor *GPBEmptyRoot_FileDescriptor(void) { typedef struct GPBEmpty__storage_ { - uint32_t _has_storage_[0]; + uint32_t _has_storage_[1]; } GPBEmpty__storage_; // This method is threadsafe because it is initially called @@ -45,14 +48,8 @@ typedef struct GPBEmpty__storage_ { file:GPBEmptyRoot_FileDescriptor() fields:NULL fieldCount:0 - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 storageSize:sizeof(GPBEmpty__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -62,4 +59,6 @@ typedef struct GPBEmpty__storage_ { @end +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/FieldMask.pbobjc.h b/objectivec/google/protobuf/FieldMask.pbobjc.h index f861a9863..73cbd8a58 100644 --- a/objectivec/google/protobuf/FieldMask.pbobjc.h +++ b/objectivec/google/protobuf/FieldMask.pbobjc.h @@ -3,12 +3,15 @@ #import "GPBProtocolBuffers.h" -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 +#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001 #error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. #endif // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CF_EXTERN_C_BEGIN NS_ASSUME_NONNULL_BEGIN @@ -81,7 +84,7 @@ typedef GPB_ENUM(GPBFieldMask_FieldNumber) { /// operation applies to all fields (as if a FieldMask of all fields /// had been specified). /// -/// Note that a field mask does not necessarily applies to the +/// Note that a field mask does not necessarily apply to the /// top-level response message. In case of a REST get operation, the /// field mask applies directly to the response, but in case of a REST /// list operation, the mask instead applies to each individual message @@ -154,6 +157,33 @@ typedef GPB_ENUM(GPBFieldMask_FieldNumber) { /// { /// mask: "user.displayName,photo" /// } +/// +/// # Field Masks and Oneof Fields +/// +/// Field masks treat fields in oneofs just as regular fields. Consider the +/// following message: +/// +/// message SampleMessage { +/// oneof test_oneof { +/// string name = 4; +/// SubMessage sub_message = 9; +/// } +/// } +/// +/// The field mask can be: +/// +/// mask { +/// paths: "name" +/// } +/// +/// Or: +/// +/// mask { +/// paths: "sub_message" +/// } +/// +/// Note that oneof type names ("test_oneof" in this case) cannot be used in +/// paths. @interface GPBFieldMask : GPBMessage /// The set of field mask paths. @@ -167,4 +197,6 @@ NS_ASSUME_NONNULL_END CF_EXTERN_C_END +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/FieldMask.pbobjc.m b/objectivec/google/protobuf/FieldMask.pbobjc.m index f9684f514..8c241afc3 100644 --- a/objectivec/google/protobuf/FieldMask.pbobjc.m +++ b/objectivec/google/protobuf/FieldMask.pbobjc.m @@ -5,6 +5,9 @@ #import "google/protobuf/FieldMask.pbobjc.h" // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma mark - GPBFieldMaskRoot @implementation GPBFieldMaskRoot @@ -44,14 +47,12 @@ typedef struct GPBFieldMask__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "pathsArray", + .dataTypeSpecific.className = NULL, .number = GPBFieldMask_FieldNumber_PathsArray, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBFieldMask__storage_, pathsArray), .flags = GPBFieldRepeated, .dataType = GPBDataTypeString, - .offset = offsetof(GPBFieldMask__storage_, pathsArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -59,15 +60,9 @@ typedef struct GPBFieldMask__storage_ { rootClass:[GPBFieldMaskRoot class] file:GPBFieldMaskRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBFieldMask__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -77,4 +72,6 @@ typedef struct GPBFieldMask__storage_ { @end +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/SourceContext.pbobjc.h b/objectivec/google/protobuf/SourceContext.pbobjc.h index 546db6749..8775348e9 100644 --- a/objectivec/google/protobuf/SourceContext.pbobjc.h +++ b/objectivec/google/protobuf/SourceContext.pbobjc.h @@ -3,12 +3,15 @@ #import "GPBProtocolBuffers.h" -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 +#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001 #error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. #endif // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CF_EXTERN_C_BEGIN NS_ASSUME_NONNULL_BEGIN @@ -46,4 +49,6 @@ NS_ASSUME_NONNULL_END CF_EXTERN_C_END +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/SourceContext.pbobjc.m b/objectivec/google/protobuf/SourceContext.pbobjc.m index ac1827fc9..950071264 100644 --- a/objectivec/google/protobuf/SourceContext.pbobjc.m +++ b/objectivec/google/protobuf/SourceContext.pbobjc.m @@ -5,6 +5,9 @@ #import "google/protobuf/SourceContext.pbobjc.h" // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma mark - GPBSourceContextRoot @implementation GPBSourceContextRoot @@ -44,14 +47,12 @@ typedef struct GPBSourceContext__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "fileName", + .dataTypeSpecific.className = NULL, .number = GPBSourceContext_FieldNumber_FileName, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBSourceContext__storage_, fileName), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBSourceContext__storage_, fileName), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -59,15 +60,9 @@ typedef struct GPBSourceContext__storage_ { rootClass:[GPBSourceContextRoot class] file:GPBSourceContextRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBSourceContext__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -77,4 +72,6 @@ typedef struct GPBSourceContext__storage_ { @end +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Struct.pbobjc.h b/objectivec/google/protobuf/Struct.pbobjc.h index 7b9c45a03..3924b4b75 100644 --- a/objectivec/google/protobuf/Struct.pbobjc.h +++ b/objectivec/google/protobuf/Struct.pbobjc.h @@ -3,12 +3,15 @@ #import "GPBProtocolBuffers.h" -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 +#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001 #error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. #endif // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CF_EXTERN_C_BEGIN @class GPBListValue; @@ -67,7 +70,7 @@ typedef GPB_ENUM(GPBStruct_FieldNumber) { /// The JSON representation for `Struct` is JSON object. @interface GPBStruct : GPBMessage -/// Map of dynamically typed values. +/// Unordered map of dynamically typed values. @property(nonatomic, readwrite, strong, null_resettable) NSMutableDictionary *fields; /// The number of items in @c fields without causing the array to be created. @property(nonatomic, readonly) NSUInteger fields_Count; @@ -159,4 +162,6 @@ NS_ASSUME_NONNULL_END CF_EXTERN_C_END +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Struct.pbobjc.m b/objectivec/google/protobuf/Struct.pbobjc.m index 14b8f271c..609400274 100644 --- a/objectivec/google/protobuf/Struct.pbobjc.m +++ b/objectivec/google/protobuf/Struct.pbobjc.m @@ -5,6 +5,9 @@ #import "google/protobuf/Struct.pbobjc.h" // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma mark - GPBStructRoot @implementation GPBStructRoot @@ -30,13 +33,20 @@ static GPBFileDescriptor *GPBStructRoot_FileDescriptor(void) { GPBEnumDescriptor *GPBNullValue_EnumDescriptor(void) { static GPBEnumDescriptor *descriptor = NULL; if (!descriptor) { - static GPBMessageEnumValueDescription values[] = { - { .name = "NullValue", .number = GPBNullValue_NullValue }, + static const char *valueNames = + "NullValue\000"; + static const int32_t values[] = { + GPBNullValue_NullValue, }; - descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBNullValue) - values:values - valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription) - enumVerifier:GPBNullValue_IsValidValue]; + GPBEnumDescriptor *worker = + [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBNullValue) + valueNames:valueNames + values:values + count:(uint32_t)(sizeof(values) / sizeof(int32_t)) + enumVerifier:GPBNullValue_IsValidValue]; + if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) { + [worker release]; + } } return descriptor; } @@ -69,14 +79,12 @@ typedef struct GPBStruct__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "fields", + .dataTypeSpecific.className = GPBStringifySymbol(GPBValue), .number = GPBStruct_FieldNumber_Fields, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBStruct__storage_, fields), .flags = GPBFieldMapKeyString, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBStruct__storage_, fields), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBValue), - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -84,15 +92,9 @@ typedef struct GPBStruct__storage_ { rootClass:[GPBStructRoot class] file:GPBStructRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBStruct__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -115,7 +117,6 @@ typedef struct GPBStruct__storage_ { typedef struct GPBValue__storage_ { uint32_t _has_storage_[2]; - BOOL boolValue; GPBNullValue nullValue; NSString *stringValue; GPBStruct *structValue; @@ -128,78 +129,60 @@ typedef struct GPBValue__storage_ { + (GPBDescriptor *)descriptor { static GPBDescriptor *descriptor = nil; if (!descriptor) { - static GPBMessageOneofDescription oneofs[] = { - { - .name = "kind", - .index = -1, - }, - }; static GPBMessageFieldDescription fields[] = { { .name = "nullValue", + .dataTypeSpecific.enumDescFunc = GPBNullValue_EnumDescriptor, .number = GPBValue_FieldNumber_NullValue, .hasIndex = -1, + .offset = (uint32_t)offsetof(GPBValue__storage_, nullValue), .flags = GPBFieldOptional | GPBFieldHasEnumDescriptor, .dataType = GPBDataTypeEnum, - .offset = offsetof(GPBValue__storage_, nullValue), - .defaultValue.valueEnum = GPBNullValue_NullValue, - .dataTypeSpecific.enumDescFunc = GPBNullValue_EnumDescriptor, - .fieldOptions = NULL, }, { .name = "numberValue", + .dataTypeSpecific.className = NULL, .number = GPBValue_FieldNumber_NumberValue, .hasIndex = -1, + .offset = (uint32_t)offsetof(GPBValue__storage_, numberValue), .flags = GPBFieldOptional, .dataType = GPBDataTypeDouble, - .offset = offsetof(GPBValue__storage_, numberValue), - .defaultValue.valueDouble = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "stringValue", + .dataTypeSpecific.className = NULL, .number = GPBValue_FieldNumber_StringValue, .hasIndex = -1, + .offset = (uint32_t)offsetof(GPBValue__storage_, stringValue), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBValue__storage_, stringValue), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "boolValue", + .dataTypeSpecific.className = NULL, .number = GPBValue_FieldNumber_BoolValue, .hasIndex = -1, + .offset = 0, // Stored in _has_storage_ to save space. .flags = GPBFieldOptional, .dataType = GPBDataTypeBool, - .offset = offsetof(GPBValue__storage_, boolValue), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "structValue", + .dataTypeSpecific.className = GPBStringifySymbol(GPBStruct), .number = GPBValue_FieldNumber_StructValue, .hasIndex = -1, + .offset = (uint32_t)offsetof(GPBValue__storage_, structValue), .flags = GPBFieldOptional, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBValue__storage_, structValue), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBStruct), - .fieldOptions = NULL, }, { .name = "listValue", + .dataTypeSpecific.className = GPBStringifySymbol(GPBListValue), .number = GPBValue_FieldNumber_ListValue, .hasIndex = -1, + .offset = (uint32_t)offsetof(GPBValue__storage_, listValue), .flags = GPBFieldOptional, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBValue__storage_, listValue), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBListValue), - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -207,15 +190,15 @@ typedef struct GPBValue__storage_ { rootClass:[GPBStructRoot class] file:GPBStructRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:oneofs - oneofCount:sizeof(oneofs) / sizeof(GPBMessageOneofDescription) - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBValue__storage_) - wireFormat:NO]; + flags:0]; + static const char *oneofs[] = { + "kind", + }; + [localDescriptor setupOneofs:oneofs + count:(uint32_t)(sizeof(oneofs) / sizeof(char*)) + firstHasIndex:-1]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -239,7 +222,7 @@ void SetGPBValue_NullValue_RawValue(GPBValue *message, int32_t value) { void GPBValue_ClearKindOneOfCase(GPBValue *message) { GPBDescriptor *descriptor = [message descriptor]; GPBOneofDescriptor *oneof = descriptor->oneofs_[0]; - GPBMaybeClearOneof(message, oneof, 0); + GPBMaybeClearOneof(message, oneof, -1, 0); } #pragma mark - GPBListValue @@ -260,14 +243,12 @@ typedef struct GPBListValue__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "valuesArray", + .dataTypeSpecific.className = GPBStringifySymbol(GPBValue), .number = GPBListValue_FieldNumber_ValuesArray, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBListValue__storage_, valuesArray), .flags = GPBFieldRepeated, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBListValue__storage_, valuesArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBValue), - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -275,15 +256,9 @@ typedef struct GPBListValue__storage_ { rootClass:[GPBStructRoot class] file:GPBStructRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBListValue__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -293,4 +268,6 @@ typedef struct GPBListValue__storage_ { @end +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Timestamp.pbobjc.h b/objectivec/google/protobuf/Timestamp.pbobjc.h index d17c28605..925dca840 100644 --- a/objectivec/google/protobuf/Timestamp.pbobjc.h +++ b/objectivec/google/protobuf/Timestamp.pbobjc.h @@ -3,12 +3,15 @@ #import "GPBProtocolBuffers.h" -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 +#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001 #error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. #endif // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CF_EXTERN_C_BEGIN NS_ASSUME_NONNULL_BEGIN @@ -105,4 +108,6 @@ NS_ASSUME_NONNULL_END CF_EXTERN_C_END +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Timestamp.pbobjc.m b/objectivec/google/protobuf/Timestamp.pbobjc.m index a206f159d..f35e435df 100644 --- a/objectivec/google/protobuf/Timestamp.pbobjc.m +++ b/objectivec/google/protobuf/Timestamp.pbobjc.m @@ -5,6 +5,9 @@ #import "google/protobuf/Timestamp.pbobjc.h" // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma mark - GPBTimestampRoot @implementation GPBTimestampRoot @@ -46,25 +49,21 @@ typedef struct GPBTimestamp__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "seconds", + .dataTypeSpecific.className = NULL, .number = GPBTimestamp_FieldNumber_Seconds, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBTimestamp__storage_, seconds), .flags = GPBFieldOptional, .dataType = GPBDataTypeInt64, - .offset = offsetof(GPBTimestamp__storage_, seconds), - .defaultValue.valueInt64 = 0LL, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "nanos", + .dataTypeSpecific.className = NULL, .number = GPBTimestamp_FieldNumber_Nanos, .hasIndex = 1, + .offset = (uint32_t)offsetof(GPBTimestamp__storage_, nanos), .flags = GPBFieldOptional, .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBTimestamp__storage_, nanos), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -72,15 +71,9 @@ typedef struct GPBTimestamp__storage_ { rootClass:[GPBTimestampRoot class] file:GPBTimestampRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBTimestamp__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -90,4 +83,6 @@ typedef struct GPBTimestamp__storage_ { @end +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Type.pbobjc.h b/objectivec/google/protobuf/Type.pbobjc.h index 9301e4f41..590d970bf 100644 --- a/objectivec/google/protobuf/Type.pbobjc.h +++ b/objectivec/google/protobuf/Type.pbobjc.h @@ -3,12 +3,15 @@ #import "GPBProtocolBuffers.h" -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 +#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001 #error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. #endif // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CF_EXTERN_C_BEGIN @class GPBAny; @@ -365,4 +368,6 @@ NS_ASSUME_NONNULL_END CF_EXTERN_C_END +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Type.pbobjc.m b/objectivec/google/protobuf/Type.pbobjc.m index b4e0a5f6f..5554a222b 100644 --- a/objectivec/google/protobuf/Type.pbobjc.m +++ b/objectivec/google/protobuf/Type.pbobjc.m @@ -7,6 +7,9 @@ #import "google/protobuf/SourceContext.pbobjc.h" // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma mark - GPBTypeRoot @implementation GPBTypeRoot @@ -45,14 +48,21 @@ static GPBFileDescriptor *GPBTypeRoot_FileDescriptor(void) { GPBEnumDescriptor *GPBSyntax_EnumDescriptor(void) { static GPBEnumDescriptor *descriptor = NULL; if (!descriptor) { - static GPBMessageEnumValueDescription values[] = { - { .name = "SyntaxProto2", .number = GPBSyntax_SyntaxProto2 }, - { .name = "SyntaxProto3", .number = GPBSyntax_SyntaxProto3 }, + static const char *valueNames = + "SyntaxProto2\000SyntaxProto3\000"; + static const int32_t values[] = { + GPBSyntax_SyntaxProto2, + GPBSyntax_SyntaxProto3, }; - descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBSyntax) - values:values - valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription) - enumVerifier:GPBSyntax_IsValidValue]; + GPBEnumDescriptor *worker = + [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBSyntax) + valueNames:valueNames + values:values + count:(uint32_t)(sizeof(values) / sizeof(int32_t)) + enumVerifier:GPBSyntax_IsValidValue]; + if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) { + [worker release]; + } } return descriptor; } @@ -96,69 +106,57 @@ typedef struct GPBType__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "name", + .dataTypeSpecific.className = NULL, .number = GPBType_FieldNumber_Name, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBType__storage_, name), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBType__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "fieldsArray", + .dataTypeSpecific.className = GPBStringifySymbol(GPBField), .number = GPBType_FieldNumber_FieldsArray, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBType__storage_, fieldsArray), .flags = GPBFieldRepeated, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBType__storage_, fieldsArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBField), - .fieldOptions = NULL, }, { .name = "oneofsArray", + .dataTypeSpecific.className = NULL, .number = GPBType_FieldNumber_OneofsArray, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBType__storage_, oneofsArray), .flags = GPBFieldRepeated, .dataType = GPBDataTypeString, - .offset = offsetof(GPBType__storage_, oneofsArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "optionsArray", + .dataTypeSpecific.className = GPBStringifySymbol(GPBOption), .number = GPBType_FieldNumber_OptionsArray, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBType__storage_, optionsArray), .flags = GPBFieldRepeated, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBType__storage_, optionsArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBOption), - .fieldOptions = NULL, }, { .name = "sourceContext", + .dataTypeSpecific.className = GPBStringifySymbol(GPBSourceContext), .number = GPBType_FieldNumber_SourceContext, - .hasIndex = 4, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GPBType__storage_, sourceContext), .flags = GPBFieldOptional, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBType__storage_, sourceContext), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBSourceContext), - .fieldOptions = NULL, }, { .name = "syntax", + .dataTypeSpecific.enumDescFunc = GPBSyntax_EnumDescriptor, .number = GPBType_FieldNumber_Syntax, - .hasIndex = 5, + .hasIndex = 2, + .offset = (uint32_t)offsetof(GPBType__storage_, syntax), .flags = GPBFieldOptional | GPBFieldHasEnumDescriptor, .dataType = GPBDataTypeEnum, - .offset = offsetof(GPBType__storage_, syntax), - .defaultValue.valueEnum = GPBSyntax_SyntaxProto2, - .dataTypeSpecific.enumDescFunc = GPBSyntax_EnumDescriptor, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -166,15 +164,9 @@ typedef struct GPBType__storage_ { rootClass:[GPBTypeRoot class] file:GPBTypeRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBType__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -212,7 +204,6 @@ void SetGPBType_Syntax_RawValue(GPBType *message, int32_t value) { typedef struct GPBField__storage_ { uint32_t _has_storage_[1]; - BOOL packed; GPBField_Kind kind; GPBField_Cardinality cardinality; int32_t number; @@ -232,139 +223,108 @@ typedef struct GPBField__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "kind", + .dataTypeSpecific.enumDescFunc = GPBField_Kind_EnumDescriptor, .number = GPBField_FieldNumber_Kind, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBField__storage_, kind), .flags = GPBFieldOptional | GPBFieldHasEnumDescriptor, .dataType = GPBDataTypeEnum, - .offset = offsetof(GPBField__storage_, kind), - .defaultValue.valueEnum = GPBField_Kind_TypeUnknown, - .dataTypeSpecific.enumDescFunc = GPBField_Kind_EnumDescriptor, - .fieldOptions = NULL, }, { .name = "cardinality", + .dataTypeSpecific.enumDescFunc = GPBField_Cardinality_EnumDescriptor, .number = GPBField_FieldNumber_Cardinality, .hasIndex = 1, + .offset = (uint32_t)offsetof(GPBField__storage_, cardinality), .flags = GPBFieldOptional | GPBFieldHasEnumDescriptor, .dataType = GPBDataTypeEnum, - .offset = offsetof(GPBField__storage_, cardinality), - .defaultValue.valueEnum = GPBField_Cardinality_CardinalityUnknown, - .dataTypeSpecific.enumDescFunc = GPBField_Cardinality_EnumDescriptor, - .fieldOptions = NULL, }, { .name = "number", + .dataTypeSpecific.className = NULL, .number = GPBField_FieldNumber_Number, .hasIndex = 2, + .offset = (uint32_t)offsetof(GPBField__storage_, number), .flags = GPBFieldOptional, .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBField__storage_, number), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "name", + .dataTypeSpecific.className = NULL, .number = GPBField_FieldNumber_Name, .hasIndex = 3, + .offset = (uint32_t)offsetof(GPBField__storage_, name), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBField__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "typeURL", + .dataTypeSpecific.className = NULL, .number = GPBField_FieldNumber_TypeURL, .hasIndex = 4, + .offset = (uint32_t)offsetof(GPBField__storage_, typeURL), .flags = GPBFieldOptional | GPBFieldTextFormatNameCustom, .dataType = GPBDataTypeString, - .offset = offsetof(GPBField__storage_, typeURL), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "oneofIndex", + .dataTypeSpecific.className = NULL, .number = GPBField_FieldNumber_OneofIndex, .hasIndex = 5, + .offset = (uint32_t)offsetof(GPBField__storage_, oneofIndex), .flags = GPBFieldOptional, .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBField__storage_, oneofIndex), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "packed", + .dataTypeSpecific.className = NULL, .number = GPBField_FieldNumber_Packed, .hasIndex = 6, + .offset = 7, // Stored in _has_storage_ to save space. .flags = GPBFieldOptional, .dataType = GPBDataTypeBool, - .offset = offsetof(GPBField__storage_, packed), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "optionsArray", + .dataTypeSpecific.className = GPBStringifySymbol(GPBOption), .number = GPBField_FieldNumber_OptionsArray, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBField__storage_, optionsArray), .flags = GPBFieldRepeated, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBField__storage_, optionsArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBOption), - .fieldOptions = NULL, }, { .name = "jsonName", + .dataTypeSpecific.className = NULL, .number = GPBField_FieldNumber_JsonName, .hasIndex = 8, + .offset = (uint32_t)offsetof(GPBField__storage_, jsonName), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBField__storage_, jsonName), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "defaultValue", + .dataTypeSpecific.className = NULL, .number = GPBField_FieldNumber_DefaultValue, .hasIndex = 9, + .offset = (uint32_t)offsetof(GPBField__storage_, defaultValue), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBField__storage_, defaultValue), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; - static GPBMessageEnumDescription enums[] = { - { .enumDescriptorFunc = GPBField_Kind_EnumDescriptor }, - { .enumDescriptorFunc = GPBField_Cardinality_EnumDescriptor }, - }; -#if GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS - const char *extraTextFormatInfo = NULL; -#else - static const char *extraTextFormatInfo = "\001\006\004\241!!\000"; -#endif // GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS GPBDescriptor *localDescriptor = [GPBDescriptor allocDescriptorForClass:[GPBField class] rootClass:[GPBTypeRoot class] file:GPBTypeRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:enums - enumCount:sizeof(enums) / sizeof(GPBMessageEnumDescription) - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBField__storage_) - wireFormat:NO - extraTextFormatInfo:extraTextFormatInfo]; + flags:0]; +#if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS + static const char *extraTextFormatInfo = + "\001\006\004\241!!\000"; + [localDescriptor setupExtraTextInfo:extraTextFormatInfo]; +#endif // !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -402,31 +362,43 @@ void SetGPBField_Cardinality_RawValue(GPBField *message, int32_t value) { GPBEnumDescriptor *GPBField_Kind_EnumDescriptor(void) { static GPBEnumDescriptor *descriptor = NULL; if (!descriptor) { - static GPBMessageEnumValueDescription values[] = { - { .name = "TypeUnknown", .number = GPBField_Kind_TypeUnknown }, - { .name = "TypeDouble", .number = GPBField_Kind_TypeDouble }, - { .name = "TypeFloat", .number = GPBField_Kind_TypeFloat }, - { .name = "TypeInt64", .number = GPBField_Kind_TypeInt64 }, - { .name = "TypeUint64", .number = GPBField_Kind_TypeUint64 }, - { .name = "TypeInt32", .number = GPBField_Kind_TypeInt32 }, - { .name = "TypeFixed64", .number = GPBField_Kind_TypeFixed64 }, - { .name = "TypeFixed32", .number = GPBField_Kind_TypeFixed32 }, - { .name = "TypeBool", .number = GPBField_Kind_TypeBool }, - { .name = "TypeString", .number = GPBField_Kind_TypeString }, - { .name = "TypeGroup", .number = GPBField_Kind_TypeGroup }, - { .name = "TypeMessage", .number = GPBField_Kind_TypeMessage }, - { .name = "TypeBytes", .number = GPBField_Kind_TypeBytes }, - { .name = "TypeUint32", .number = GPBField_Kind_TypeUint32 }, - { .name = "TypeEnum", .number = GPBField_Kind_TypeEnum }, - { .name = "TypeSfixed32", .number = GPBField_Kind_TypeSfixed32 }, - { .name = "TypeSfixed64", .number = GPBField_Kind_TypeSfixed64 }, - { .name = "TypeSint32", .number = GPBField_Kind_TypeSint32 }, - { .name = "TypeSint64", .number = GPBField_Kind_TypeSint64 }, + static const char *valueNames = + "TypeUnknown\000TypeDouble\000TypeFloat\000TypeInt" + "64\000TypeUint64\000TypeInt32\000TypeFixed64\000Type" + "Fixed32\000TypeBool\000TypeString\000TypeGroup\000Ty" + "peMessage\000TypeBytes\000TypeUint32\000TypeEnum\000" + "TypeSfixed32\000TypeSfixed64\000TypeSint32\000Typ" + "eSint64\000"; + static const int32_t values[] = { + GPBField_Kind_TypeUnknown, + GPBField_Kind_TypeDouble, + GPBField_Kind_TypeFloat, + GPBField_Kind_TypeInt64, + GPBField_Kind_TypeUint64, + GPBField_Kind_TypeInt32, + GPBField_Kind_TypeFixed64, + GPBField_Kind_TypeFixed32, + GPBField_Kind_TypeBool, + GPBField_Kind_TypeString, + GPBField_Kind_TypeGroup, + GPBField_Kind_TypeMessage, + GPBField_Kind_TypeBytes, + GPBField_Kind_TypeUint32, + GPBField_Kind_TypeEnum, + GPBField_Kind_TypeSfixed32, + GPBField_Kind_TypeSfixed64, + GPBField_Kind_TypeSint32, + GPBField_Kind_TypeSint64, }; - descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBField_Kind) - values:values - valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription) - enumVerifier:GPBField_Kind_IsValidValue]; + GPBEnumDescriptor *worker = + [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBField_Kind) + valueNames:valueNames + values:values + count:(uint32_t)(sizeof(values) / sizeof(int32_t)) + enumVerifier:GPBField_Kind_IsValidValue]; + if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) { + [worker release]; + } } return descriptor; } @@ -463,16 +435,24 @@ BOOL GPBField_Kind_IsValidValue(int32_t value__) { GPBEnumDescriptor *GPBField_Cardinality_EnumDescriptor(void) { static GPBEnumDescriptor *descriptor = NULL; if (!descriptor) { - static GPBMessageEnumValueDescription values[] = { - { .name = "CardinalityUnknown", .number = GPBField_Cardinality_CardinalityUnknown }, - { .name = "CardinalityOptional", .number = GPBField_Cardinality_CardinalityOptional }, - { .name = "CardinalityRequired", .number = GPBField_Cardinality_CardinalityRequired }, - { .name = "CardinalityRepeated", .number = GPBField_Cardinality_CardinalityRepeated }, + static const char *valueNames = + "CardinalityUnknown\000CardinalityOptional\000C" + "ardinalityRequired\000CardinalityRepeated\000"; + static const int32_t values[] = { + GPBField_Cardinality_CardinalityUnknown, + GPBField_Cardinality_CardinalityOptional, + GPBField_Cardinality_CardinalityRequired, + GPBField_Cardinality_CardinalityRepeated, }; - descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBField_Cardinality) - values:values - valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription) - enumVerifier:GPBField_Cardinality_IsValidValue]; + GPBEnumDescriptor *worker = + [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBField_Cardinality) + valueNames:valueNames + values:values + count:(uint32_t)(sizeof(values) / sizeof(int32_t)) + enumVerifier:GPBField_Cardinality_IsValidValue]; + if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) { + [worker release]; + } } return descriptor; } @@ -516,58 +496,48 @@ typedef struct GPBEnum__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "name", + .dataTypeSpecific.className = NULL, .number = GPBEnum_FieldNumber_Name, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBEnum__storage_, name), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBEnum__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "enumvalueArray", + .dataTypeSpecific.className = GPBStringifySymbol(GPBEnumValue), .number = GPBEnum_FieldNumber_EnumvalueArray, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBEnum__storage_, enumvalueArray), .flags = GPBFieldRepeated, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBEnum__storage_, enumvalueArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBEnumValue), - .fieldOptions = NULL, }, { .name = "optionsArray", + .dataTypeSpecific.className = GPBStringifySymbol(GPBOption), .number = GPBEnum_FieldNumber_OptionsArray, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBEnum__storage_, optionsArray), .flags = GPBFieldRepeated, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBEnum__storage_, optionsArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBOption), - .fieldOptions = NULL, }, { .name = "sourceContext", + .dataTypeSpecific.className = GPBStringifySymbol(GPBSourceContext), .number = GPBEnum_FieldNumber_SourceContext, - .hasIndex = 3, + .hasIndex = 1, + .offset = (uint32_t)offsetof(GPBEnum__storage_, sourceContext), .flags = GPBFieldOptional, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBEnum__storage_, sourceContext), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBSourceContext), - .fieldOptions = NULL, }, { .name = "syntax", + .dataTypeSpecific.enumDescFunc = GPBSyntax_EnumDescriptor, .number = GPBEnum_FieldNumber_Syntax, - .hasIndex = 4, + .hasIndex = 2, + .offset = (uint32_t)offsetof(GPBEnum__storage_, syntax), .flags = GPBFieldOptional | GPBFieldHasEnumDescriptor, .dataType = GPBDataTypeEnum, - .offset = offsetof(GPBEnum__storage_, syntax), - .defaultValue.valueEnum = GPBSyntax_SyntaxProto2, - .dataTypeSpecific.enumDescFunc = GPBSyntax_EnumDescriptor, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -575,15 +545,9 @@ typedef struct GPBEnum__storage_ { rootClass:[GPBTypeRoot class] file:GPBTypeRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBEnum__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -627,36 +591,30 @@ typedef struct GPBEnumValue__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "name", + .dataTypeSpecific.className = NULL, .number = GPBEnumValue_FieldNumber_Name, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBEnumValue__storage_, name), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBEnumValue__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "number", + .dataTypeSpecific.className = NULL, .number = GPBEnumValue_FieldNumber_Number, .hasIndex = 1, + .offset = (uint32_t)offsetof(GPBEnumValue__storage_, number), .flags = GPBFieldOptional, .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBEnumValue__storage_, number), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "optionsArray", + .dataTypeSpecific.className = GPBStringifySymbol(GPBOption), .number = GPBEnumValue_FieldNumber_OptionsArray, .hasIndex = GPBNoHasBit, + .offset = (uint32_t)offsetof(GPBEnumValue__storage_, optionsArray), .flags = GPBFieldRepeated, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBEnumValue__storage_, optionsArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBOption), - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -664,15 +622,9 @@ typedef struct GPBEnumValue__storage_ { rootClass:[GPBTypeRoot class] file:GPBTypeRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBEnumValue__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -702,25 +654,21 @@ typedef struct GPBOption__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "name", + .dataTypeSpecific.className = NULL, .number = GPBOption_FieldNumber_Name, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBOption__storage_, name), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBOption__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, { .name = "value", + .dataTypeSpecific.className = GPBStringifySymbol(GPBAny), .number = GPBOption_FieldNumber_Value, .hasIndex = 1, + .offset = (uint32_t)offsetof(GPBOption__storage_, value), .flags = GPBFieldOptional, .dataType = GPBDataTypeMessage, - .offset = offsetof(GPBOption__storage_, value), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(GPBAny), - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -728,15 +676,9 @@ typedef struct GPBOption__storage_ { rootClass:[GPBTypeRoot class] file:GPBTypeRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBOption__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -746,4 +688,6 @@ typedef struct GPBOption__storage_ { @end +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Wrappers.pbobjc.h b/objectivec/google/protobuf/Wrappers.pbobjc.h index 38b99622f..465105005 100644 --- a/objectivec/google/protobuf/Wrappers.pbobjc.h +++ b/objectivec/google/protobuf/Wrappers.pbobjc.h @@ -3,12 +3,15 @@ #import "GPBProtocolBuffers.h" -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 +#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30001 #error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. #endif // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CF_EXTERN_C_BEGIN NS_ASSUME_NONNULL_BEGIN @@ -174,4 +177,6 @@ NS_ASSUME_NONNULL_END CF_EXTERN_C_END +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/objectivec/google/protobuf/Wrappers.pbobjc.m b/objectivec/google/protobuf/Wrappers.pbobjc.m index 0403b4645..5cc6c2e40 100644 --- a/objectivec/google/protobuf/Wrappers.pbobjc.m +++ b/objectivec/google/protobuf/Wrappers.pbobjc.m @@ -5,6 +5,9 @@ #import "google/protobuf/Wrappers.pbobjc.h" // @@protoc_insertion_point(imports) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + #pragma mark - GPBWrappersRoot @implementation GPBWrappersRoot @@ -44,14 +47,12 @@ typedef struct GPBDoubleValue__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "value", + .dataTypeSpecific.className = NULL, .number = GPBDoubleValue_FieldNumber_Value, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBDoubleValue__storage_, value), .flags = GPBFieldOptional, .dataType = GPBDataTypeDouble, - .offset = offsetof(GPBDoubleValue__storage_, value), - .defaultValue.valueDouble = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -59,15 +60,9 @@ typedef struct GPBDoubleValue__storage_ { rootClass:[GPBWrappersRoot class] file:GPBWrappersRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBDoubleValue__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -95,14 +90,12 @@ typedef struct GPBFloatValue__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "value", + .dataTypeSpecific.className = NULL, .number = GPBFloatValue_FieldNumber_Value, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBFloatValue__storage_, value), .flags = GPBFieldOptional, .dataType = GPBDataTypeFloat, - .offset = offsetof(GPBFloatValue__storage_, value), - .defaultValue.valueFloat = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -110,15 +103,9 @@ typedef struct GPBFloatValue__storage_ { rootClass:[GPBWrappersRoot class] file:GPBWrappersRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBFloatValue__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -146,14 +133,12 @@ typedef struct GPBInt64Value__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "value", + .dataTypeSpecific.className = NULL, .number = GPBInt64Value_FieldNumber_Value, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBInt64Value__storage_, value), .flags = GPBFieldOptional, .dataType = GPBDataTypeInt64, - .offset = offsetof(GPBInt64Value__storage_, value), - .defaultValue.valueInt64 = 0LL, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -161,15 +146,9 @@ typedef struct GPBInt64Value__storage_ { rootClass:[GPBWrappersRoot class] file:GPBWrappersRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBInt64Value__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -197,14 +176,12 @@ typedef struct GPBUInt64Value__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "value", + .dataTypeSpecific.className = NULL, .number = GPBUInt64Value_FieldNumber_Value, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBUInt64Value__storage_, value), .flags = GPBFieldOptional, .dataType = GPBDataTypeUInt64, - .offset = offsetof(GPBUInt64Value__storage_, value), - .defaultValue.valueUInt64 = 0ULL, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -212,15 +189,9 @@ typedef struct GPBUInt64Value__storage_ { rootClass:[GPBWrappersRoot class] file:GPBWrappersRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBUInt64Value__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -248,14 +219,12 @@ typedef struct GPBInt32Value__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "value", + .dataTypeSpecific.className = NULL, .number = GPBInt32Value_FieldNumber_Value, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBInt32Value__storage_, value), .flags = GPBFieldOptional, .dataType = GPBDataTypeInt32, - .offset = offsetof(GPBInt32Value__storage_, value), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -263,15 +232,9 @@ typedef struct GPBInt32Value__storage_ { rootClass:[GPBWrappersRoot class] file:GPBWrappersRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBInt32Value__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -299,14 +262,12 @@ typedef struct GPBUInt32Value__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "value", + .dataTypeSpecific.className = NULL, .number = GPBUInt32Value_FieldNumber_Value, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBUInt32Value__storage_, value), .flags = GPBFieldOptional, .dataType = GPBDataTypeUInt32, - .offset = offsetof(GPBUInt32Value__storage_, value), - .defaultValue.valueUInt32 = 0U, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -314,15 +275,9 @@ typedef struct GPBUInt32Value__storage_ { rootClass:[GPBWrappersRoot class] file:GPBWrappersRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBUInt32Value__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -339,7 +294,6 @@ typedef struct GPBUInt32Value__storage_ { typedef struct GPBBoolValue__storage_ { uint32_t _has_storage_[1]; - BOOL value; } GPBBoolValue__storage_; // This method is threadsafe because it is initially called @@ -350,14 +304,12 @@ typedef struct GPBBoolValue__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "value", + .dataTypeSpecific.className = NULL, .number = GPBBoolValue_FieldNumber_Value, .hasIndex = 0, + .offset = 1, // Stored in _has_storage_ to save space. .flags = GPBFieldOptional, .dataType = GPBDataTypeBool, - .offset = offsetof(GPBBoolValue__storage_, value), - .defaultValue.valueBool = NO, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -365,15 +317,9 @@ typedef struct GPBBoolValue__storage_ { rootClass:[GPBWrappersRoot class] file:GPBWrappersRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBBoolValue__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -401,14 +347,12 @@ typedef struct GPBStringValue__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "value", + .dataTypeSpecific.className = NULL, .number = GPBStringValue_FieldNumber_Value, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBStringValue__storage_, value), .flags = GPBFieldOptional, .dataType = GPBDataTypeString, - .offset = offsetof(GPBStringValue__storage_, value), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -416,15 +360,9 @@ typedef struct GPBStringValue__storage_ { rootClass:[GPBWrappersRoot class] file:GPBWrappersRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBStringValue__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -452,14 +390,12 @@ typedef struct GPBBytesValue__storage_ { static GPBMessageFieldDescription fields[] = { { .name = "value", + .dataTypeSpecific.className = NULL, .number = GPBBytesValue_FieldNumber_Value, .hasIndex = 0, + .offset = (uint32_t)offsetof(GPBBytesValue__storage_, value), .flags = GPBFieldOptional, .dataType = GPBDataTypeBytes, - .offset = offsetof(GPBBytesValue__storage_, value), - .defaultValue.valueData = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, }, }; GPBDescriptor *localDescriptor = @@ -467,15 +403,9 @@ typedef struct GPBBytesValue__storage_ { rootClass:[GPBWrappersRoot class] file:GPBWrappersRoot_FileDescriptor() fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 + fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBBytesValue__storage_) - wireFormat:NO]; + flags:0]; NSAssert(descriptor == nil, @"Startup recursed!"); descriptor = localDescriptor; } @@ -485,4 +415,6 @@ typedef struct GPBBytesValue__storage_ { @end +#pragma clang diagnostic pop + // @@protoc_insertion_point(global_scope) diff --git a/php/ext/google/protobuf/config.m4 b/php/ext/google/protobuf/config.m4 new file mode 100644 index 000000000..b5946f74e --- /dev/null +++ b/php/ext/google/protobuf/config.m4 @@ -0,0 +1,10 @@ +dnl lines starting with "dnl" are comments + +PHP_ARG_ENABLE(protobuf, whether to enable Protobuf extension, [ --enable-protobuf Enable Protobuf extension]) + +if test "$PHP_PROTOBUF" != "no"; then + + dnl this defines the extension + PHP_NEW_EXTENSION(protobuf, upb.c protobuf.c def.c message.c storage.c, $ext_shared) + +fi diff --git a/php/ext/google/protobuf/def.c b/php/ext/google/protobuf/def.c new file mode 100644 index 000000000..fc1880695 --- /dev/null +++ b/php/ext/google/protobuf/def.c @@ -0,0 +1,381 @@ +#include "protobuf.h" + +// ----------------------------------------------------------------------------- +// Common Utilities +// ----------------------------------------------------------------------------- + +void check_upb_status(const upb_status* status, const char* msg) { + if (!upb_ok(status)) { + zend_error("%s: %s\n", msg, upb_status_errmsg(status)); + } +} + + +static upb_def *check_notfrozen(const upb_def *def) { + if (upb_def_isfrozen(def)) { + zend_error(E_ERROR, + "Attempt to modify a frozen descriptor. Once descriptors are " + "added to the descriptor pool, they may not be modified."); + } + return (upb_def *)def; +} + +static upb_msgdef *check_msgdef_notfrozen(const upb_msgdef *def) { + return upb_downcast_msgdef_mutable(check_notfrozen((const upb_def *)def)); +} + +static upb_fielddef *check_fielddef_notfrozen(const upb_fielddef *def) { + return upb_downcast_fielddef_mutable(check_notfrozen((const upb_def *)def)); +} + +#define PROTOBUF_WRAP_INTERN(wrapper, intern, intern_dtor) \ + Z_TYPE_P(wrapper) = IS_OBJECT; \ + Z_OBJVAL_P(wrapper) \ + .handle = zend_objects_store_put( \ + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \ + intern_dtor, NULL TSRMLS_CC); \ + Z_OBJVAL_P(wrapper).handlers = zend_get_std_object_handlers(); + +#define PROTOBUF_SETUP_ZEND_WRAPPER(class_name, class_name_lower, wrapper, \ + intern) \ + Z_TYPE_P(wrapper) = IS_OBJECT; \ + class_name *intern = ALLOC(class_name); \ + memset(intern, 0, sizeof(class_name)); \ + class_name_lower##_init_c_instance(intern TSRMLS_CC); \ + Z_OBJVAL_P(wrapper) \ + .handle = zend_objects_store_put(intern, NULL, class_name_lower##_free, \ + NULL TSRMLS_CC); \ + Z_OBJVAL_P(wrapper).handlers = zend_get_std_object_handlers(); + +#define PROTOBUF_CREATE_ZEND_WRAPPER(class_name, class_name_lower, wrapper, \ + intern) \ + MAKE_STD_ZVAL(wrapper); \ + PROTOBUF_SETUP_ZEND_WRAPPER(class_name, class_name_lower, wrapper, intern); + +#define DEFINE_CLASS(name, name_lower, string_name) \ + zend_class_entry *name_lower##_type; \ + void name_lower##_init(TSRMLS_D) { \ + zend_class_entry class_type; \ + INIT_CLASS_ENTRY(class_type, string_name, name_lower##_methods); \ + name_lower##_type = zend_register_internal_class(&class_type TSRMLS_CC); \ + name_lower##_type->create_object = name_lower##_create; \ + } \ + name *php_to_##name_lower(zval *val TSRMLS_DC) { \ + return (name *)zend_object_store_get_object(val TSRMLS_CC); \ + } \ + void name_lower##_free(void *object TSRMLS_DC) { \ + name *intern = (name *)object; \ + name_lower##_free_c(intern TSRMLS_CC); \ + efree(object); \ + } \ + zend_object_value name_lower##_create(zend_class_entry *ce TSRMLS_DC) { \ + zend_object_value return_value; \ + name *intern = (name *)emalloc(sizeof(name)); \ + memset(intern, 0, sizeof(name)); \ + name_lower##_init_c_instance(intern TSRMLS_CC); \ + return_value.handle = zend_objects_store_put( \ + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \ + name_lower##_free, NULL TSRMLS_CC); \ + return_value.handlers = zend_get_std_object_handlers(); \ + return return_value; \ + } + +// ----------------------------------------------------------------------------- +// DescriptorPool +// ----------------------------------------------------------------------------- + +static zend_function_entry descriptor_pool_methods[] = { + PHP_ME(DescriptorPool, addMessage, NULL, ZEND_ACC_PUBLIC) + PHP_ME(DescriptorPool, finalize, NULL, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +DEFINE_CLASS(DescriptorPool, descriptor_pool, + "Google\\Protobuf\\DescriptorPool"); + +DescriptorPool *generated_pool; // The actual generated pool + +ZEND_FUNCTION(get_generated_pool) { + if (PROTOBUF_G(generated_pool) == NULL) { + MAKE_STD_ZVAL(PROTOBUF_G(generated_pool)); + Z_TYPE_P(PROTOBUF_G(generated_pool)) = IS_OBJECT; + generated_pool = ALLOC(DescriptorPool); + descriptor_pool_init_c_instance(generated_pool TSRMLS_CC); + Z_OBJ_HANDLE_P(PROTOBUF_G(generated_pool)) = zend_objects_store_put( + generated_pool, NULL, + (zend_objects_free_object_storage_t)descriptor_pool_free, NULL TSRMLS_CC); + Z_OBJ_HT_P(PROTOBUF_G(generated_pool)) = zend_get_std_object_handlers(); + } + RETURN_ZVAL(PROTOBUF_G(generated_pool), 1, 0); +} + +void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC) { + zend_object_std_init(&pool->std, descriptor_pool_type TSRMLS_CC); + pool->symtab = upb_symtab_new(&pool->symtab); + + ALLOC_HASHTABLE(pool->pending_list); + zend_hash_init(pool->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0); +} + +void descriptor_pool_free_c(DescriptorPool *pool TSRMLS_DC) { + upb_symtab_unref(pool->symtab, &pool->symtab); + zend_hash_destroy(pool->pending_list); + FREE_HASHTABLE(pool->pending_list); +} + +PHP_METHOD(DescriptorPool, addMessage) { + char *name = NULL; + int str_len; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &str_len) == + FAILURE) { + return; + } + + zval* retval = NULL; + PROTOBUF_CREATE_ZEND_WRAPPER(MessageBuilderContext, message_builder_context, + retval, context); + + MAKE_STD_ZVAL(context->pool); + ZVAL_ZVAL(context->pool, getThis(), 1, 0); + + Descriptor *desc = php_to_descriptor(context->descriptor TSRMLS_CC); + Descriptor_name_set(desc, name); + + RETURN_ZVAL(retval, 0, 1); +} + +static void validate_msgdef(const upb_msgdef* msgdef) { + // Verify that no required fields exist. proto3 does not support these. + upb_msg_field_iter it; + for (upb_msg_field_begin(&it, msgdef); + !upb_msg_field_done(&it); + upb_msg_field_next(&it)) { + const upb_fielddef* field = upb_msg_iter_field(&it); + if (upb_fielddef_label(field) == UPB_LABEL_REQUIRED) { + zend_error(E_ERROR, "Required fields are unsupported in proto3."); + } + } +} + +PHP_METHOD(DescriptorPool, finalize) { + DescriptorPool *self = php_to_descriptor_pool(getThis() TSRMLS_CC); + Bucket *temp; + int i, num; + + num = zend_hash_num_elements(self->pending_list); + upb_def **defs = emalloc(sizeof(upb_def *) * num); + + for (i = 0, temp = self->pending_list->pListHead; temp != NULL; + temp = temp->pListNext) { + zval *def_php = *(zval **)temp->pData; + Descriptor* desc = php_to_descriptor(def_php TSRMLS_CC); + defs[i] = (upb_def *)desc->msgdef; + validate_msgdef((const upb_msgdef *)defs[i++]); + } + + CHECK_UPB(upb_symtab_add(self->symtab, (upb_def **)defs, num, NULL, &status), + "Unable to add defs to DescriptorPool"); + + for (temp = self->pending_list->pListHead; temp != NULL; + temp = temp->pListNext) { + // zval *def_php = *(zval **)temp->pData; + // Descriptor* desc = php_to_descriptor(def_php TSRMLS_CC); + build_class_from_descriptor((zval *)temp->pDataPtr TSRMLS_CC); + } + + FREE(defs); + zend_hash_destroy(self->pending_list); + zend_hash_init(self->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0); +} + +// ----------------------------------------------------------------------------- +// Descriptor +// ----------------------------------------------------------------------------- + +static zend_function_entry descriptor_methods[] = { + ZEND_FE_END +}; + +DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Descriptor"); + +void descriptor_free_c(Descriptor *self TSRMLS_DC) { + upb_msg_field_iter iter; + upb_msg_field_begin(&iter, self->msgdef); + while (!upb_msg_field_done(&iter)) { + upb_fielddef *fielddef = upb_msg_iter_field(&iter); + upb_fielddef_unref(fielddef, &fielddef); + upb_msg_field_next(&iter); + } + upb_msgdef_unref(self->msgdef, &self->msgdef); + if (self->layout) { + free_layout(self->layout); + } +} + +static void descriptor_add_field(Descriptor *desc, + const upb_fielddef *fielddef) { + upb_msgdef *mut_def = check_msgdef_notfrozen(desc->msgdef); + upb_fielddef *mut_field_def = check_fielddef_notfrozen(fielddef); + CHECK_UPB(upb_msgdef_addfield(mut_def, mut_field_def, NULL, &status), + "Adding field to Descriptor failed"); + // add_def_obj(fielddef, obj); +} + +void descriptor_init_c_instance(Descriptor* desc TSRMLS_DC) { + zend_object_std_init(&desc->std, descriptor_type TSRMLS_CC); + desc->msgdef = upb_msgdef_new(&desc->msgdef); + desc->layout = NULL; + // MAKE_STD_ZVAL(intern->klass); + // ZVAL_NULL(intern->klass); + desc->pb_serialize_handlers = NULL; +} + +void Descriptor_name_set(Descriptor *desc, const char *name) { + upb_msgdef *mut_def = check_msgdef_notfrozen(desc->msgdef); + CHECK_UPB(upb_msgdef_setfullname(mut_def, name, &status), + "Error setting Descriptor name"); +} + +// ----------------------------------------------------------------------------- +// FieldDescriptor +// ----------------------------------------------------------------------------- + +static void field_descriptor_name_set(const upb_fielddef* fielddef, + const char *name) { + upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef); + CHECK_UPB(upb_fielddef_setname(mut_def, name, &status), + "Error setting FieldDescriptor name"); +} + +static void field_descriptor_label_set(const upb_fielddef* fielddef, + upb_label_t upb_label) { + upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef); + upb_fielddef_setlabel(mut_def, upb_label); +} + +upb_fieldtype_t string_to_descriptortype(const char *type) { +#define CONVERT(upb, str) \ + if (!strcmp(type, str)) { \ + return UPB_DESCRIPTOR_TYPE_##upb; \ + } + + CONVERT(FLOAT, "float"); + CONVERT(DOUBLE, "double"); + CONVERT(BOOL, "bool"); + CONVERT(STRING, "string"); + CONVERT(BYTES, "bytes"); + CONVERT(MESSAGE, "message"); + CONVERT(GROUP, "group"); + CONVERT(ENUM, "enum"); + CONVERT(INT32, "int32"); + CONVERT(INT64, "int64"); + CONVERT(UINT32, "uint32"); + CONVERT(UINT64, "uint64"); + CONVERT(SINT32, "sint32"); + CONVERT(SINT64, "sint64"); + CONVERT(FIXED32, "fixed32"); + CONVERT(FIXED64, "fixed64"); + CONVERT(SFIXED32, "sfixed32"); + CONVERT(SFIXED64, "sfixed64"); + +#undef CONVERT + + zend_error(E_ERROR, "Unknown field type."); + return 0; +} + +static void field_descriptor_type_set(const upb_fielddef* fielddef, + const char *type) { + upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef); + upb_fielddef_setdescriptortype(mut_def, string_to_descriptortype(type)); +} + +static void field_descriptor_number_set(const upb_fielddef* fielddef, + int number) { + upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef); + CHECK_UPB(upb_fielddef_setnumber(mut_def, number, &status), + "Error setting field number"); +} + +// ----------------------------------------------------------------------------- +// MessageBuilderContext +// ----------------------------------------------------------------------------- + +static zend_function_entry message_builder_context_methods[] = { + PHP_ME(MessageBuilderContext, finalizeToPool, NULL, ZEND_ACC_PUBLIC) + PHP_ME(MessageBuilderContext, optional, NULL, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} +}; + +DEFINE_CLASS(MessageBuilderContext, message_builder_context, + "Google\\Protobuf\\Internal\\MessageBuilderContext"); + +void message_builder_context_free_c(MessageBuilderContext *context TSRMLS_DC) { + zval_ptr_dtor(&context->descriptor); + zval_ptr_dtor(&context->pool); +} + +void message_builder_context_init_c_instance( + MessageBuilderContext *context TSRMLS_DC) { + zend_object_std_init(&context->std, message_builder_context_type TSRMLS_CC); + PROTOBUF_CREATE_ZEND_WRAPPER(Descriptor, descriptor, context->descriptor, + desc); +} + +static void msgdef_add_field(Descriptor *desc, upb_label_t upb_label, + const char *name, const char *type, int number, + const char *type_class) { + upb_fielddef *fielddef = upb_fielddef_new(&fielddef); + upb_fielddef_setpacked(fielddef, false); + + field_descriptor_label_set(fielddef, upb_label); + field_descriptor_name_set(fielddef, name); + field_descriptor_type_set(fielddef, type); + field_descriptor_number_set(fielddef, number); + +// // if (type_class != Qnil) { +// // if (TYPE(type_class) != T_STRING) { +// // rb_raise(rb_eArgError, "Expected string for type class"); +// // } +// // // Make it an absolute type name by prepending a dot. +// // type_class = rb_str_append(rb_str_new2("."), type_class); +// // rb_funcall(fielddef, rb_intern("submsg_name="), 1, type_class); +// // } + descriptor_add_field(desc, fielddef); +} + +PHP_METHOD(MessageBuilderContext, optional) { + MessageBuilderContext *self = php_to_message_builder_context(getThis() TSRMLS_CC); + Descriptor *desc = php_to_descriptor(self->descriptor TSRMLS_CC); + // VALUE name, type, number, type_class; + const char *name, *type, *type_class; + int number, name_str_len, type_str_len, type_class_str_len; + if (ZEND_NUM_ARGS() == 3) { + if (zend_parse_parameters(3 TSRMLS_CC, "ssl", &name, + &name_str_len, &type, &type_str_len, &number) == FAILURE) { + return; + } + } else { + if (zend_parse_parameters(4 TSRMLS_CC, "ssls", &name, + &name_str_len, &type, &type_str_len, &number, &type_class, + &type_class_str_len) == FAILURE) { + return; + } + } + + msgdef_add_field(desc, UPB_LABEL_OPTIONAL, name, type, number, type_class); + + zval_copy_ctor(getThis()); + RETURN_ZVAL(getThis(), 1, 0); +} + +PHP_METHOD(MessageBuilderContext, finalizeToPool) { + MessageBuilderContext *self = php_to_message_builder_context(getThis() TSRMLS_CC); + DescriptorPool *pool = php_to_descriptor_pool(self->pool TSRMLS_CC); + Descriptor* desc = php_to_descriptor(self->descriptor TSRMLS_CC); + + Z_ADDREF_P(self->descriptor); + zend_hash_next_index_insert(pool->pending_list, &self->descriptor, + sizeof(zval *), NULL); + RETURN_ZVAL(self->pool, 1, 0); +} diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c new file mode 100644 index 000000000..c062d665a --- /dev/null +++ b/php/ext/google/protobuf/message.c @@ -0,0 +1,273 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2014 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 + +#include "protobuf.h" + +// ----------------------------------------------------------------------------- +// Class/module creation from msgdefs and enumdefs, respectively. +// ----------------------------------------------------------------------------- + +void* message_data(void* msg) { + return ((uint8_t *)msg) + sizeof(MessageHeader); +} + +void message_set_property(zval* object, zval* field_name, zval* value, + const zend_literal* key TSRMLS_DC) { + const upb_fielddef* field; + + MessageHeader* self = zend_object_store_get_object(object TSRMLS_CC); + + CHECK_TYPE(field_name, IS_STRING); + field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(field_name)); + if (field == NULL) { + zend_error(E_ERROR, "Unknown field: %s", Z_STRVAL_P(field_name)); + } + layout_set(self->descriptor->layout, message_data(self), field, value); +} + +zval* message_get_property(zval* object, zval* member, int type, + const zend_literal* key TSRMLS_DC) { + MessageHeader* self = + (MessageHeader*)zend_object_store_get_object(object TSRMLS_CC); + CHECK_TYPE(member, IS_STRING); + + const upb_fielddef* field; + field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(member)); + if (field == NULL) { + return EG(uninitialized_zval_ptr); + } + zval* retval = layout_get(self->descriptor->layout, message_data(self), field TSRMLS_CC); + return retval; +} + +static zend_function_entry message_methods[] = { + PHP_ME(Message, encode, NULL, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} +}; + +/* stringsink *****************************************************************/ + +// This should probably be factored into a common upb component. + +typedef struct { + upb_byteshandler handler; + upb_bytessink sink; + char *ptr; + size_t len, size; +} stringsink; + +static void *stringsink_start(void *_sink, const void *hd, size_t size_hint) { + stringsink *sink = _sink; + sink->len = 0; + return sink; +} + +static size_t stringsink_string(void *_sink, const void *hd, const char *ptr, + size_t len, const upb_bufhandle *handle) { + stringsink *sink = _sink; + size_t new_size = sink->size; + + UPB_UNUSED(hd); + UPB_UNUSED(handle); + + while (sink->len + len > new_size) { + new_size *= 2; + } + + if (new_size != sink->size) { + sink->ptr = realloc(sink->ptr, new_size); + sink->size = new_size; + } + + memcpy(sink->ptr + sink->len, ptr, len); + sink->len += len; + + return len; +} + +void stringsink_init(stringsink *sink) { + upb_byteshandler_init(&sink->handler); + upb_byteshandler_setstartstr(&sink->handler, stringsink_start, NULL); + upb_byteshandler_setstring(&sink->handler, stringsink_string, NULL); + + upb_bytessink_reset(&sink->sink, &sink->handler, sink); + + sink->size = 32; + sink->ptr = malloc(sink->size); + sink->len = 0; +} + +void stringsink_uninit(stringsink *sink) { free(sink->ptr); } + +// Stack-allocated context during an encode/decode operation. Contains the upb +// environment and its stack-based allocator, an initial buffer for allocations +// to avoid malloc() when possible, and a template for PHP exception messages +// if any error occurs. +#define STACK_ENV_STACKBYTES 4096 +typedef struct { + upb_env env; + upb_seededalloc alloc; + const char *php_error_template; + char allocbuf[STACK_ENV_STACKBYTES]; +} stackenv; + +static void stackenv_init(stackenv* se, const char* errmsg); +static void stackenv_uninit(stackenv* se); + +// Callback invoked by upb if any error occurs during parsing or serialization. +static bool env_error_func(void* ud, const upb_status* status) { + stackenv* se = ud; + // Free the env -- rb_raise will longjmp up the stack past the encode/decode + // function so it would not otherwise have been freed. + stackenv_uninit(se); + + // TODO(teboring): have a way to verify that this is actually a parse error, + // instead of just throwing "parse error" unconditionally. + zend_error(E_ERROR, se->php_error_template); + // Never reached. + return false; +} + +static void stackenv_init(stackenv* se, const char* errmsg) { + se->php_error_template = errmsg; + upb_env_init(&se->env); + upb_seededalloc_init(&se->alloc, &se->allocbuf, STACK_ENV_STACKBYTES); + upb_env_setallocfunc(&se->env, upb_seededalloc_getallocfunc(&se->alloc), + &se->alloc); + upb_env_seterrorfunc(&se->env, env_error_func, se); +} + +static void stackenv_uninit(stackenv* se) { + upb_env_uninit(&se->env); + upb_seededalloc_uninit(&se->alloc); +} + +// ----------------------------------------------------------------------------- +// Message +// ----------------------------------------------------------------------------- + +static const upb_handlers* msgdef_pb_serialize_handlers(Descriptor* desc) { + if (desc->pb_serialize_handlers == NULL) { + desc->pb_serialize_handlers = + upb_pb_encoder_newhandlers(desc->msgdef, &desc->pb_serialize_handlers); + } + return desc->pb_serialize_handlers; +} + +PHP_METHOD(Message, encode) { + Descriptor* desc = (Descriptor*)zend_object_store_get_object( + CE_STATIC_MEMBERS(Z_OBJCE_P(getThis()))[0] TSRMLS_CC); + + stringsink sink; + stringsink_init(&sink); + + { + const upb_handlers* serialize_handlers = msgdef_pb_serialize_handlers(desc); + + stackenv se; + upb_pb_encoder* encoder; + + stackenv_init(&se, "Error occurred during encoding: %s"); + encoder = upb_pb_encoder_create(&se.env, serialize_handlers, &sink.sink); + + putmsg(getThis(), desc, upb_pb_encoder_input(encoder), 0); + + RETVAL_STRINGL(sink.ptr, sink.len, 1); + + stackenv_uninit(&se); + stringsink_uninit(&sink); + } +} + +void message_free(void * object TSRMLS_DC) { + FREE(object); +} + +zend_object_value message_create(zend_class_entry* ce TSRMLS_DC) { + zend_object_value return_value; + + zval* php_descriptor = get_def_obj(ce); + + Descriptor* desc = zend_object_store_get_object(php_descriptor TSRMLS_CC); + MessageHeader* msg = (MessageHeader*)ALLOC_N( + uint8_t, sizeof(MessageHeader) + desc->layout->size); + memset(message_data(msg), 0, desc->layout->size); + + // We wrap first so that everything in the message object is GC-rooted in case + // a collection happens during object creation in layout_init(). + msg->descriptor = desc; + + layout_init(desc->layout, message_data(msg)); + zend_object_std_init(&msg->std, ce TSRMLS_CC); + + return_value.handle = zend_objects_store_put( + msg, (zend_objects_store_dtor_t)zend_objects_destroy_object, + message_free, NULL TSRMLS_CC); + + return_value.handlers = PROTOBUF_G(message_handlers); + return return_value; +} + +const zend_class_entry* build_class_from_descriptor( + zval* php_descriptor TSRMLS_DC) { + Descriptor* desc = php_to_descriptor(php_descriptor); + if (desc->layout == NULL) { + MessageLayout* layout = create_layout(desc->msgdef); + desc->layout = layout; + } + // TODO(teboring): Add it back. + // if (desc->fill_method == NULL) { + // desc->fill_method = new_fillmsg_decodermethod(desc, &desc->fill_method); + // } + + const char* name = upb_msgdef_fullname(desc->msgdef); + if (name == NULL) { + zend_error(E_ERROR, "Descriptor does not have assigned name."); + } + + zend_class_entry class_entry; + INIT_CLASS_ENTRY_EX(class_entry, name, strlen(name), message_methods); + zend_class_entry* registered_ce = + zend_register_internal_class(&class_entry TSRMLS_CC); + + add_def_obj(registered_ce, php_descriptor); + + if (PROTOBUF_G(message_handlers) == NULL) { + PROTOBUF_G(message_handlers) = ALLOC(zend_object_handlers); + memcpy(PROTOBUF_G(message_handlers), zend_get_std_object_handlers(), + sizeof(zend_object_handlers)); + PROTOBUF_G(message_handlers)->write_property = message_set_property; + PROTOBUF_G(message_handlers)->read_property = message_get_property; + } + + registered_ce->create_object = message_create; +} diff --git a/php/ext/google/protobuf/protobuf.c b/php/ext/google/protobuf/protobuf.c new file mode 100644 index 000000000..b1ace8b06 --- /dev/null +++ b/php/ext/google/protobuf/protobuf.c @@ -0,0 +1,89 @@ +#include "protobuf.h" + +#include + +ZEND_DECLARE_MODULE_GLOBALS(protobuf) +static PHP_GINIT_FUNCTION(protobuf); +static PHP_GSHUTDOWN_FUNCTION(protobuf); + +// ----------------------------------------------------------------------------- +// Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor +// instances. +// ----------------------------------------------------------------------------- + +void add_def_obj(const void* def, zval* value) { + uint nIndex = (ulong)def & PROTOBUF_G(upb_def_to_php_obj_map).nTableMask; + + zval* pDest = NULL; + Z_ADDREF_P(value); + zend_hash_index_update(&PROTOBUF_G(upb_def_to_php_obj_map), (zend_ulong)def, + &value, sizeof(zval*), &pDest); +} + +zval* get_def_obj(const void* def) { + zval** value; + if (zend_hash_index_find(&PROTOBUF_G(upb_def_to_php_obj_map), (zend_ulong)def, + &value) == FAILURE) { + zend_error(E_ERROR, "PHP object not found for given definition.\n"); + return NULL; + } + return *value; +} + +// ----------------------------------------------------------------------------- +// Utilities. +// ----------------------------------------------------------------------------- + +// define the function(s) we want to add +zend_function_entry protobuf_functions[] = { + ZEND_FE(get_generated_pool, NULL) + ZEND_FE_END +}; + +// "protobuf_functions" refers to the struct defined above +// we'll be filling in more of this later: you can use this to specify +// globals, php.ini info, startup and teardown functions, etc. +zend_module_entry protobuf_module_entry = { + STANDARD_MODULE_HEADER, + PHP_PROTOBUF_EXTNAME, // extension name + protobuf_functions, // function list + PHP_MINIT(protobuf), // process startup + NULL, // process shutdown + NULL, // request startup + NULL, // request shutdown + NULL, // extension info + PHP_PROTOBUF_VERSION, // extension version + PHP_MODULE_GLOBALS(protobuf), // globals descriptor + PHP_GINIT(protobuf), // globals ctor + PHP_GSHUTDOWN(protobuf), // globals dtor + NULL, // post deactivate + STANDARD_MODULE_PROPERTIES_EX +}; + +// install module +ZEND_GET_MODULE(protobuf) + +// global variables +static PHP_GINIT_FUNCTION(protobuf) { + protobuf_globals->generated_pool = NULL; + generated_pool = NULL; + protobuf_globals->message_handlers = NULL; + zend_hash_init(&protobuf_globals->upb_def_to_php_obj_map, 16, NULL, + ZVAL_PTR_DTOR, 0); +} + +static PHP_GSHUTDOWN_FUNCTION(protobuf) { + if (protobuf_globals->generated_pool != NULL) { + FREE_ZVAL(protobuf_globals->generated_pool); + } + if (protobuf_globals->message_handlers != NULL) { + FREE(protobuf_globals->message_handlers); + } + zend_hash_destroy(&protobuf_globals->upb_def_to_php_obj_map); +} + +PHP_MINIT_FUNCTION(protobuf) { + descriptor_pool_init(TSRMLS_C); + descriptor_init(TSRMLS_C); + message_builder_context_init(TSRMLS_C); +} diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h new file mode 100644 index 000000000..f9038550a --- /dev/null +++ b/php/ext/google/protobuf/protobuf.h @@ -0,0 +1,281 @@ +#ifndef __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__ +#define __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__ + +#include + +#include "upb.h" + +#define PHP_PROTOBUF_EXTNAME "protobuf" +#define PHP_PROTOBUF_VERSION "0.01" + +// Forward decls. +struct DescriptorPool; +struct Descriptor; +struct FieldDescriptor; +struct EnumDescriptor; +struct MessageLayout; +struct MessageField; +struct MessageHeader; +struct MessageBuilderContext; +struct EnumBuilderContext; + +typedef struct DescriptorPool DescriptorPool; +typedef struct Descriptor Descriptor; +typedef struct FieldDescriptor FieldDescriptor; +typedef struct OneofDescriptor OneofDescriptor; +typedef struct EnumDescriptor EnumDescriptor; +typedef struct MessageLayout MessageLayout; +typedef struct MessageField MessageField; +typedef struct MessageHeader MessageHeader; +typedef struct MessageBuilderContext MessageBuilderContext; +typedef struct OneofBuilderContext OneofBuilderContext; +typedef struct EnumBuilderContext EnumBuilderContext; + +extern zend_class_entry* builder_type; +extern zend_class_entry* descriptor_type; +extern zend_class_entry* message_builder_context_type; + +extern DescriptorPool* generated_pool; // The actual generated pool + +ZEND_BEGIN_MODULE_GLOBALS(protobuf) + zval* generated_pool; + zend_object_handlers* message_handlers; + HashTable upb_def_to_php_obj_map; +ZEND_END_MODULE_GLOBALS(protobuf) + +ZEND_DECLARE_MODULE_GLOBALS(protobuf) + +#ifdef ZTS +#define PROTOBUF_G(v) TSRMG(protobuf_globals_id, zend_protobuf_globals*, v) +#else +#define PROTOBUF_G(v) (protobuf_globals.v) +#endif + +// ----------------------------------------------------------------------------- +// PHP functions and global variables. +// ----------------------------------------------------------------------------- + +PHP_MINIT_FUNCTION(protobuf); + +// ----------------------------------------------------------------------------- +// PHP class structure. +// ----------------------------------------------------------------------------- + +struct DescriptorPool { + zend_object std; + upb_symtab* symtab; + HashTable* pending_list; +}; + +struct Descriptor { + zend_object std; + const upb_msgdef* msgdef; + MessageLayout* layout; + // zval* klass; // begins as NULL + // const upb_handlers* fill_handlers; + // const upb_pbdecodermethod* fill_method; + const upb_handlers* pb_serialize_handlers; + // const upb_handlers* json_serialize_handlers; + // Handlers hold type class references for sub-message fields directly in some + // cases. We need to keep these rooted because they might otherwise be + // collected. + // zval_array typeclass_references; +}; + +struct FieldDescriptor { + zend_object std; + const upb_fielddef* fielddef; +}; + +struct OneofDescriptor { + zend_object std; + const upb_oneofdef* oneofdef; +}; + +struct EnumDescriptor { + zend_object std; + const upb_enumdef* enumdef; + // zval* module; // begins as NULL +}; + +// ----------------------------------------------------------------------------- +// Native slot storage abstraction. +// ----------------------------------------------------------------------------- + +#define NATIVE_SLOT_MAX_SIZE sizeof(uint64_t) + +size_t native_slot_size(upb_fieldtype_t type); + +#define MAP_KEY_FIELD 1 +#define MAP_VALUE_FIELD 2 + +// Oneof case slot value to indicate that no oneof case is set. The value `0` is +// safe because field numbers are used as case identifiers, and no field can +// have a number of 0. +#define ONEOF_CASE_NONE 0 + +// These operate on a map field (i.e., a repeated field of submessages whose +// submessage type is a map-entry msgdef). +bool is_map_field(const upb_fielddef* field); +const upb_fielddef* map_field_key(const upb_fielddef* field); +const upb_fielddef* map_field_value(const upb_fielddef* field); + +// These operate on a map-entry msgdef. +const upb_fielddef* map_entry_key(const upb_msgdef* msgdef); +const upb_fielddef* map_entry_value(const upb_msgdef* msgdef); + +// ----------------------------------------------------------------------------- +// Message layout / storage. +// ----------------------------------------------------------------------------- + +#define MESSAGE_FIELD_NO_CASE ((size_t)-1) + +struct MessageField { + size_t offset; + size_t case_offset; // for oneofs, a uint32. Else, MESSAGE_FIELD_NO_CASE. +}; + +struct MessageLayout { + const upb_msgdef* msgdef; + MessageField* fields; + size_t size; +}; + +void layout_init(MessageLayout* layout, void* storage); +zval* layout_get(MessageLayout* layout, const void* storage, + const upb_fielddef* field TSRMLS_DC); +MessageLayout* create_layout(const upb_msgdef* msgdef); +void free_layout(MessageLayout* layout); +zval* native_slot_get(upb_fieldtype_t type, /*VALUE type_class,*/ + const void* memory TSRMLS_DC); + +// ----------------------------------------------------------------------------- +// Message class creation. +// ----------------------------------------------------------------------------- + +struct MessageHeader { + zend_object std; + Descriptor* descriptor; // kept alive by self.class.descriptor reference. + // Data comes after this. +}; + +struct MessageBuilderContext { + zend_object std; + zval* descriptor; + zval* pool; +}; + +struct OneofBuilderContext { + zend_object std; + // VALUE descriptor; + // VALUE builder; +}; + +struct EnumBuilderContext { + zend_object std; + // VALUE enumdesc; +}; + +// Forward-declare all of the PHP method implementations. + +DescriptorPool* php_to_descriptor_pool(zval* value TSRMLS_DC); +zend_object_value descriptor_pool_create(zend_class_entry *ce TSRMLS_DC); +void descriptor_pool_free_c(DescriptorPool* object TSRMLS_DC); +void descriptor_pool_free(void* object TSRMLS_DC); +void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC); +PHP_METHOD(DescriptorPool, addMessage); +PHP_METHOD(DescriptorPool, finalize); + +Descriptor* php_to_descriptor(zval* value TSRMLS_DC); +zend_object_value descriptor_create(zend_class_entry *ce TSRMLS_DC); +void descriptor_init_c_instance(Descriptor* intern TSRMLS_DC); +void descriptor_free_c(Descriptor* object TSRMLS_DC); +void descriptor_free(void* object TSRMLS_DC); +void descriptor_name_set(Descriptor *desc, const char *name); + +MessageBuilderContext* php_to_message_builder_context(zval* value TSRMLS_DC); +zend_object_value message_builder_context_create( + zend_class_entry* ce TSRMLS_DC); +void message_builder_context_init_c_instance( + MessageBuilderContext* intern TSRMLS_DC); +void message_builder_context_free_c(MessageBuilderContext* object TSRMLS_DC); +void message_builder_context_free(void* object TSRMLS_DC); +PHP_METHOD(MessageBuilderContext, optional); +PHP_METHOD(MessageBuilderContext, finalizeToPool); + +PHP_METHOD(Message, encode); +const zend_class_entry* build_class_from_descriptor( + zval* php_descriptor TSRMLS_DC); + +PHP_FUNCTION(get_generated_pool); + +// ----------------------------------------------------------------------------- +// Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor +// instances. +// ---------------------------------------------------------------------------- + +void add_def_obj(const void* def, zval* value); +zval* get_def_obj(const void* def); + +// ----------------------------------------------------------------------------- +// Utilities. +// ----------------------------------------------------------------------------- + +// PHP Array utils. +#define Z_ARRVAL_SIZE_P(zval_p) zend_hash_num_elements(Z_ARRVAL_P(zval_p)) +#define Z_ARRVAL_BEGIN_P(zval_p) Z_ARRVAL_P(zval_p)->pListHead +#define Z_BUCKET_NEXT_PP(bucket_pp) *bucket_pp = (*bucket_pp)->pListNext + +#define DEFINE_PHP_OBJECT(class_name, class_name_lower, name) \ + do { \ + zval* name; \ + MAKE_STD_ZVAL(name); \ + object_init_ex(name, class_name_lower##_type); \ + } while (0) + +#define DEFINE_PHP_WRAPPER(class_name, class_name_lower, name, intern) \ + zval* name; \ + MAKE_STD_ZVAL(name); \ + object_init_ex(name, class_name_lower##_type); \ + Z_OBJVAL_P(name) \ + .handle = zend_objects_store_put( \ + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \ + class_name_lower##_free, NULL TSRMLS_CC); + +#define DEFINE_PHP_ZVAL(name) \ + do { \ + zval* name; \ + MAKE_STD_ZVAL(name); \ + } while (0) + +#define DEFINE_PHP_STRING(name, value) \ + do { \ + zval* name; \ + MAKE_STD_ZVAL(name); \ + ZVAL_STRING(name, value, 1); \ + } while (0) + +// Upb Utilities + +void check_upb_status(const upb_status* status, const char* msg); + +#define CHECK_UPB(code, msg) \ + do { \ + upb_status status = UPB_STATUS_INIT; \ + code; \ + check_upb_status(&status, msg); \ + } while (0) + +// Memory management + +#define ALLOC(class_name) (class_name*) emalloc(sizeof(class_name)) +#define ALLOC_N(class_name, n) (class_name*) emalloc(sizeof(class_name) * n) +#define FREE(object) efree(object) + +// Type Checking +#define CHECK_TYPE(field, type) \ + if (Z_TYPE_P(field) != type) { \ + zend_error(E_ERROR, "Unexpected type"); \ + } + +#endif // __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__ diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c new file mode 100644 index 000000000..e5a09c17f --- /dev/null +++ b/php/ext/google/protobuf/storage.c @@ -0,0 +1,539 @@ +#include +#include + +// ----------------------------------------------------------------------------- +// PHP <-> native slot management. +// ----------------------------------------------------------------------------- + +static zval* int32_to_zval(int32_t value) { + zval* tmp; + MAKE_STD_ZVAL(tmp); + ZVAL_LONG(tmp, value); + php_printf("int32 to zval\n"); + // ZVAL_LONG(tmp, 1); + return tmp; +} + +#define DEREF(memory, type) *(type*)(memory) + +size_t native_slot_size(upb_fieldtype_t type) { + switch (type) { + case UPB_TYPE_FLOAT: return 4; + case UPB_TYPE_DOUBLE: return 8; + case UPB_TYPE_BOOL: return 1; + case UPB_TYPE_STRING: return sizeof(zval*); + case UPB_TYPE_BYTES: return sizeof(zval*); + case UPB_TYPE_MESSAGE: return sizeof(zval*); + case UPB_TYPE_ENUM: return 4; + case UPB_TYPE_INT32: return 4; + case UPB_TYPE_INT64: return 8; + case UPB_TYPE_UINT32: return 4; + case UPB_TYPE_UINT64: return 8; + default: return 0; + } +} + +static bool is_php_num(zval* value) { + // Is numerial string also valid? + return (Z_TYPE_P(value) == IS_LONG || + Z_TYPE_P(value) == IS_DOUBLE); +} + +void native_slot_check_int_range_precision(upb_fieldtype_t type, zval* val) { + // TODO(teboring): Add it back. + // if (!is_php_num(val)) { + // zend_error(E_ERROR, "Expected number type for integral field."); + // } + + // if (Z_TYPE_P(val) == IS_DOUBLE) { + // double dbl_val = NUM2DBL(val); + // if (floor(dbl_val) != dbl_val) { + // zend_error(E_ERROR, + // "Non-integral floating point value assigned to integer field."); + // } + // } + // if (type == UPB_TYPE_UINT32 || type == UPB_TYPE_UINT64) { + // if (NUM2DBL(val) < 0) { + // zend_error(E_ERROR, + // "Assigning negative value to unsigned integer field."); + // } + // } +} + +zval* native_slot_get(upb_fieldtype_t type, /*VALUE type_class,*/ + const void* memory TSRMLS_DC) { + zval* retval = NULL; + switch (type) { + // TODO(teboring): Add it back. + // case UPB_TYPE_FLOAT: + // return DBL2NUM(DEREF(memory, float)); + // case UPB_TYPE_DOUBLE: + // return DBL2NUM(DEREF(memory, double)); + // case UPB_TYPE_BOOL: + // return DEREF(memory, int8_t) ? Qtrue : Qfalse; + // case UPB_TYPE_STRING: + // case UPB_TYPE_BYTES: + // case UPB_TYPE_MESSAGE: + // return DEREF(memory, VALUE); + // case UPB_TYPE_ENUM: { + // int32_t val = DEREF(memory, int32_t); + // VALUE symbol = enum_lookup(type_class, INT2NUM(val)); + // if (symbol == Qnil) { + // return INT2NUM(val); + // } else { + // return symbol; + // } + // } + case UPB_TYPE_INT32: + return int32_to_zval(DEREF(memory, int32_t)); + // TODO(teboring): Add it back. + // case UPB_TYPE_INT64: + // return LL2NUM(DEREF(memory, int64_t)); + // case UPB_TYPE_UINT32: + // return UINT2NUM(DEREF(memory, uint32_t)); + // case UPB_TYPE_UINT64: + // return ULL2NUM(DEREF(memory, uint64_t)); + default: + return EG(uninitialized_zval_ptr); + } +} + +void native_slot_init(upb_fieldtype_t type, void* memory) { + switch (type) { + case UPB_TYPE_FLOAT: + DEREF(memory, float) = 0.0; + break; + case UPB_TYPE_DOUBLE: + DEREF(memory, double) = 0.0; + break; + case UPB_TYPE_BOOL: + DEREF(memory, int8_t) = 0; + break; + // TODO(teboring): Add it back. + // case UPB_TYPE_STRING: + // case UPB_TYPE_BYTES: + // DEREF(memory, VALUE) = php_str_new2(""); + // php_enc_associate(DEREF(memory, VALUE), (type == UPB_TYPE_BYTES) + // ? kRubyString8bitEncoding + // : kRubyStringUtf8Encoding); + // break; + // case UPB_TYPE_MESSAGE: + // DEREF(memory, VALUE) = Qnil; + // break; + case UPB_TYPE_ENUM: + case UPB_TYPE_INT32: + DEREF(memory, int32_t) = 0; + break; + case UPB_TYPE_INT64: + DEREF(memory, int64_t) = 0; + break; + case UPB_TYPE_UINT32: + DEREF(memory, uint32_t) = 0; + break; + case UPB_TYPE_UINT64: + DEREF(memory, uint64_t) = 0; + break; + default: + break; + } +} + +void native_slot_set(upb_fieldtype_t type, /*VALUE type_class,*/ void* memory, + zval* value) { + native_slot_set_value_and_case(type, /*type_class,*/ memory, value, NULL, 0); +} + +void native_slot_set_value_and_case(upb_fieldtype_t type, /*VALUE type_class,*/ + void* memory, zval* value, + uint32_t* case_memory, + uint32_t case_number) { + switch (type) { + case UPB_TYPE_FLOAT: + if (!Z_TYPE_P(value) == IS_LONG) { + zend_error(E_ERROR, "Expected number type for float field."); + } + DEREF(memory, float) = Z_DVAL_P(value); + break; + case UPB_TYPE_DOUBLE: + // TODO(teboring): Add it back. + // if (!is_php_num(value)) { + // zend_error(E_ERROR, "Expected number type for double field."); + // } + // DEREF(memory, double) = Z_DVAL_P(value); + break; + case UPB_TYPE_BOOL: { + int8_t val = -1; + if (zval_is_true(value)) { + val = 1; + } else { + val = 0; + } + // TODO(teboring): Add it back. + // else if (value == Qfalse) { + // val = 0; + // } + // else { + // php_raise(php_eTypeError, "Invalid argument for boolean field."); + // } + DEREF(memory, int8_t) = val; + break; + } + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: { + // TODO(teboring): Add it back. + // if (Z_TYPE_P(value) != IS_STRING) { + // zend_error(E_ERROR, "Invalid argument for string field."); + // } + // native_slot_validate_string_encoding(type, value); + // DEREF(memory, zval*) = value; + break; + } + case UPB_TYPE_MESSAGE: { + // TODO(teboring): Add it back. + // if (CLASS_OF(value) == CLASS_OF(Qnil)) { + // value = Qnil; + // } else if (CLASS_OF(value) != type_class) { + // php_raise(php_eTypeError, + // "Invalid type %s to assign to submessage field.", + // php_class2name(CLASS_OF(value))); + // } + // DEREF(memory, VALUE) = value; + break; + } + case UPB_TYPE_ENUM: { + // TODO(teboring): Add it back. + // int32_t int_val = 0; + // if (!is_php_num(value) && TYPE(value) != T_SYMBOL) { + // php_raise(php_eTypeError, + // "Expected number or symbol type for enum field."); + // } + // if (TYPE(value) == T_SYMBOL) { + // // Ensure that the given symbol exists in the enum module. + // VALUE lookup = php_funcall(type_class, php_intern("resolve"), 1, value); + // if (lookup == Qnil) { + // php_raise(php_eRangeError, "Unknown symbol value for enum field."); + // } else { + // int_val = NUM2INT(lookup); + // } + // } else { + // native_slot_check_int_range_precision(UPB_TYPE_INT32, value); + // int_val = NUM2INT(value); + // } + // DEREF(memory, int32_t) = int_val; + // break; + } + case UPB_TYPE_INT32: + case UPB_TYPE_INT64: + case UPB_TYPE_UINT32: + case UPB_TYPE_UINT64: + native_slot_check_int_range_precision(type, value); + switch (type) { + case UPB_TYPE_INT32: + php_printf("Setting INT32 field\n"); + DEREF(memory, int32_t) = Z_LVAL_P(value); + break; + case UPB_TYPE_INT64: + // TODO(teboring): Add it back. + // DEREF(memory, int64_t) = NUM2LL(value); + break; + case UPB_TYPE_UINT32: + // TODO(teboring): Add it back. + // DEREF(memory, uint32_t) = NUM2UINT(value); + break; + case UPB_TYPE_UINT64: + // TODO(teboring): Add it back. + // DEREF(memory, uint64_t) = NUM2ULL(value); + break; + default: + break; + } + break; + default: + break; + } + + if (case_memory != NULL) { + *case_memory = case_number; + } +} + +// ----------------------------------------------------------------------------- +// Map field utilities. +// ---------------------------------------------------------------------------- + +const upb_msgdef* tryget_map_entry_msgdef(const upb_fielddef* field) { + const upb_msgdef* subdef; + if (upb_fielddef_label(field) != UPB_LABEL_REPEATED || + upb_fielddef_type(field) != UPB_TYPE_MESSAGE) { + return NULL; + } + subdef = upb_fielddef_msgsubdef(field); + return upb_msgdef_mapentry(subdef) ? subdef : NULL; +} + +const upb_msgdef* map_entry_msgdef(const upb_fielddef* field) { + const upb_msgdef* subdef = tryget_map_entry_msgdef(field); + assert(subdef); + return subdef; +} + +bool is_map_field(const upb_fielddef* field) { + return tryget_map_entry_msgdef(field) != NULL; +} + +// ----------------------------------------------------------------------------- +// Memory layout management. +// ----------------------------------------------------------------------------- + +static size_t align_up_to(size_t offset, size_t granularity) { + // Granularity must be a power of two. + return (offset + granularity - 1) & ~(granularity - 1); +} + +MessageLayout* create_layout(const upb_msgdef* msgdef) { + MessageLayout* layout = ALLOC(MessageLayout); + int nfields = upb_msgdef_numfields(msgdef); + upb_msg_field_iter it; + upb_msg_oneof_iter oit; + size_t off = 0; + + layout->fields = ALLOC_N(MessageField, nfields); + + for (upb_msg_field_begin(&it, msgdef); !upb_msg_field_done(&it); + upb_msg_field_next(&it)) { + const upb_fielddef* field = upb_msg_iter_field(&it); + size_t field_size; + + if (upb_fielddef_containingoneof(field)) { + // Oneofs are handled separately below. + continue; + } + + // Allocate |field_size| bytes for this field in the layout. + field_size = 0; + if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + field_size = sizeof(zval*); + } else { + field_size = native_slot_size(upb_fielddef_type(field)); + } + + // Align current offset up to | size | granularity. + off = align_up_to(off, field_size); + layout->fields[upb_fielddef_index(field)].offset = off; + layout->fields[upb_fielddef_index(field)].case_offset = + MESSAGE_FIELD_NO_CASE; + off += field_size; + } + + // Handle oneofs now -- we iterate over oneofs specifically and allocate only + // one slot per oneof. + // + // We assign all value slots first, then pack the 'case' fields at the end, + // since in the common case (modern 64-bit platform) these are 8 bytes and 4 + // bytes respectively and we want to avoid alignment overhead. + // + // Note that we reserve 4 bytes (a uint32) per 'case' slot because the value + // space for oneof cases is conceptually as wide as field tag numbers. In + // practice, it's unlikely that a oneof would have more than e.g. 256 or 64K + // members (8 or 16 bits respectively), so conceivably we could assign + // consecutive case numbers and then pick a smaller oneof case slot size, but + // the complexity to implement this indirection is probably not worthwhile. + for (upb_msg_oneof_begin(&oit, msgdef); !upb_msg_oneof_done(&oit); + upb_msg_oneof_next(&oit)) { + const upb_oneofdef* oneof = upb_msg_iter_oneof(&oit); + upb_oneof_iter fit; + + // Always allocate NATIVE_SLOT_MAX_SIZE bytes, but share the slot between + // all fields. + size_t field_size = NATIVE_SLOT_MAX_SIZE; + // Align the offset . + off = align_up_to( off, field_size); + // Assign all fields in the oneof this same offset. + for (upb_oneof_begin(&fit, oneof); !upb_oneof_done(&fit); + upb_oneof_next(&fit)) { + const upb_fielddef* field = upb_oneof_iter_field(&fit); + layout->fields[upb_fielddef_index(field)].offset = off; + } + off += field_size; + } + + // Now the case fields. + for (upb_msg_oneof_begin(&oit, msgdef); !upb_msg_oneof_done(&oit); + upb_msg_oneof_next(&oit)) { + const upb_oneofdef* oneof = upb_msg_iter_oneof(&oit); + upb_oneof_iter fit; + + size_t field_size = sizeof(uint32_t); + // Align the offset . + off = (off + field_size - 1) & ~(field_size - 1); + // Assign all fields in the oneof this same offset. + for (upb_oneof_begin(&fit, oneof); !upb_oneof_done(&fit); + upb_oneof_next(&fit)) { + const upb_fielddef* field = upb_oneof_iter_field(&fit); + layout->fields[upb_fielddef_index(field)].case_offset = off; + } + off += field_size; + } + + layout->size = off; + + layout->msgdef = msgdef; + upb_msgdef_ref(layout->msgdef, &layout->msgdef); + + return layout; +} + +void free_layout(MessageLayout* layout) { + FREE(layout->fields); + upb_msgdef_unref(layout->msgdef, &layout->msgdef); + FREE(layout); +} + +// TODO(teboring): Add it back. +// VALUE field_type_class(const upb_fielddef* field) { +// VALUE type_class = Qnil; +// if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE) { +// VALUE submsgdesc = get_def_obj(upb_fielddef_subdef(field)); +// type_class = Descriptor_msgclass(submsgdesc); +// } else if (upb_fielddef_type(field) == UPB_TYPE_ENUM) { +// VALUE subenumdesc = get_def_obj(upb_fielddef_subdef(field)); +// type_class = EnumDescriptor_enummodule(subenumdesc); +// } +// return type_class; +// } + +static void* slot_memory(MessageLayout* layout, const void* storage, + const upb_fielddef* field) { + return ((uint8_t*)storage) + layout->fields[upb_fielddef_index(field)].offset; +} + +static uint32_t* slot_oneof_case(MessageLayout* layout, const void* storage, + const upb_fielddef* field) { + return (uint32_t*)(((uint8_t*)storage) + + layout->fields[upb_fielddef_index(field)].case_offset); +} + +void layout_set(MessageLayout* layout, void* storage, const upb_fielddef* field, + zval* val) { + void* memory = slot_memory(layout, storage, field); + uint32_t* oneof_case = slot_oneof_case(layout, storage, field); + + if (upb_fielddef_containingoneof(field)) { + if (Z_TYPE_P(val) == IS_NULL) { + // Assigning nil to a oneof field clears the oneof completely. + *oneof_case = ONEOF_CASE_NONE; + memset(memory, 0, NATIVE_SLOT_MAX_SIZE); + } else { + // The transition between field types for a single oneof (union) slot is + // somewhat complex because we need to ensure that a GC triggered at any + // point by a call into the Ruby VM sees a valid state for this field and + // does not either go off into the weeds (following what it thinks is a + // VALUE but is actually a different field type) or miss an object (seeing + // what it thinks is a primitive field but is actually a VALUE for the new + // field type). + // + // In order for the transition to be safe, the oneof case slot must be in + // sync with the value slot whenever the Ruby VM has been called. Thus, we + // use native_slot_set_value_and_case(), which ensures that both the value + // and case number are altered atomically (w.r.t. the Ruby VM). + native_slot_set_value_and_case(upb_fielddef_type(field), + /*field_type_class(field),*/ memory, val, + oneof_case, upb_fielddef_number(field)); + } + } else if (is_map_field(field)) { + // TODO(teboring): Add it back. + // check_map_field_type(val, field); + // DEREF(memory, zval*) = val; + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + // TODO(teboring): Add it back. + // check_repeated_field_type(val, field); + // DEREF(memory, zval*) = val; + } else { + native_slot_set(upb_fielddef_type(field), /*field_type_class(field),*/ memory, + val); + } +} + +void layout_init(MessageLayout* layout, void* storage) { + upb_msg_field_iter it; + for (upb_msg_field_begin(&it, layout->msgdef); !upb_msg_field_done(&it); + upb_msg_field_next(&it)) { + const upb_fielddef* field = upb_msg_iter_field(&it); + void* memory = slot_memory(layout, storage, field); + uint32_t* oneof_case = slot_oneof_case(layout, storage, field); + + if (upb_fielddef_containingoneof(field)) { + // TODO(teboring): Add it back. + // memset(memory, 0, NATIVE_SLOT_MAX_SIZE); + // *oneof_case = ONEOF_CASE_NONE; + } else if (is_map_field(field)) { + // TODO(teboring): Add it back. + // VALUE map = Qnil; + + // const upb_fielddef* key_field = map_field_key(field); + // const upb_fielddef* value_field = map_field_value(field); + // VALUE type_class = field_type_class(value_field); + + // if (type_class != Qnil) { + // VALUE args[3] = { + // fieldtype_to_php(upb_fielddef_type(key_field)), + // fieldtype_to_php(upb_fielddef_type(value_field)), type_class, + // }; + // map = php_class_new_instance(3, args, cMap); + // } else { + // VALUE args[2] = { + // fieldtype_to_php(upb_fielddef_type(key_field)), + // fieldtype_to_php(upb_fielddef_type(value_field)), + // }; + // map = php_class_new_instance(2, args, cMap); + // } + + // DEREF(memory, VALUE) = map; + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + // TODO(teboring): Add it back. + // VALUE ary = Qnil; + + // VALUE type_class = field_type_class(field); + + // if (type_class != Qnil) { + // VALUE args[2] = { + // fieldtype_to_php(upb_fielddef_type(field)), type_class, + // }; + // ary = php_class_new_instance(2, args, cRepeatedField); + // } else { + // VALUE args[1] = {fieldtype_to_php(upb_fielddef_type(field))}; + // ary = php_class_new_instance(1, args, cRepeatedField); + // } + + // DEREF(memory, VALUE) = ary; + } else { + native_slot_init(upb_fielddef_type(field), memory); + } + } +} + +zval* layout_get(MessageLayout* layout, const void* storage, + const upb_fielddef* field TSRMLS_DC) { + void* memory = slot_memory(layout, storage, field); + uint32_t* oneof_case = slot_oneof_case(layout, storage, field); + + if (upb_fielddef_containingoneof(field)) { + if (*oneof_case != upb_fielddef_number(field)) { + return NULL; + // TODO(teboring): Add it back. + // return Qnil; + } + return NULL; + // TODO(teboring): Add it back. + // return native_slot_get(upb_fielddef_type(field), field_type_class(field), + // memory); + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + return NULL; + // TODO(teboring): Add it back. + // return *((VALUE*)memory); + } else { + return native_slot_get( + upb_fielddef_type(field), /*field_type_class(field), */ + memory TSRMLS_CC); + } +} diff --git a/php/ext/google/protobuf/test.php b/php/ext/google/protobuf/test.php new file mode 100644 index 000000000..6bbadd48b --- /dev/null +++ b/php/ext/google/protobuf/test.php @@ -0,0 +1,15 @@ +addMessage("TestMessage") + ->optional("optional_int32_a", "int32", 1) + ->optional("optional_int32_b", "int32", 2) + ->finalizeToPool() + ->finalize(); + +$test_message = new \TestMessage(); + +?> diff --git a/php/ext/google/protobuf/upb.c b/php/ext/google/protobuf/upb.c new file mode 100644 index 000000000..048a163a9 --- /dev/null +++ b/php/ext/google/protobuf/upb.c @@ -0,0 +1,11990 @@ +// Amalgamated source file +#include "upb.h" + + +#include +#include + +typedef struct { + size_t len; + char str[1]; /* Null-terminated string data follows. */ +} str_t; + +static str_t *newstr(const char *data, size_t len) { + str_t *ret = malloc(sizeof(*ret) + len); + if (!ret) return NULL; + ret->len = len; + memcpy(ret->str, data, len); + ret->str[len] = '\0'; + return ret; +} + +static void freestr(str_t *s) { free(s); } + +/* isalpha() etc. from are locale-dependent, which we don't want. */ +static bool upb_isbetween(char c, char low, char high) { + return c >= low && c <= high; +} + +static bool upb_isletter(char c) { + return upb_isbetween(c, 'A', 'Z') || upb_isbetween(c, 'a', 'z') || c == '_'; +} + +static bool upb_isalphanum(char c) { + return upb_isletter(c) || upb_isbetween(c, '0', '9'); +} + +static bool upb_isident(const char *str, size_t len, bool full, upb_status *s) { + bool start = true; + size_t i; + for (i = 0; i < len; i++) { + char c = str[i]; + if (c == '.') { + if (start || !full) { + upb_status_seterrf(s, "invalid name: unexpected '.' (%s)", str); + return false; + } + start = true; + } else if (start) { + if (!upb_isletter(c)) { + upb_status_seterrf( + s, "invalid name: path components must start with a letter (%s)", + str); + return false; + } + start = false; + } else { + if (!upb_isalphanum(c)) { + upb_status_seterrf(s, "invalid name: non-alphanumeric character (%s)", + str); + return false; + } + } + } + return !start; +} + + +/* upb_def ********************************************************************/ + +upb_deftype_t upb_def_type(const upb_def *d) { return d->type; } + +const char *upb_def_fullname(const upb_def *d) { return d->fullname; } + +bool upb_def_setfullname(upb_def *def, const char *fullname, upb_status *s) { + assert(!upb_def_isfrozen(def)); + if (!upb_isident(fullname, strlen(fullname), true, s)) return false; + free((void*)def->fullname); + def->fullname = upb_strdup(fullname); + return true; +} + +upb_def *upb_def_dup(const upb_def *def, const void *o) { + switch (def->type) { + case UPB_DEF_MSG: + return upb_msgdef_upcast_mutable( + upb_msgdef_dup(upb_downcast_msgdef(def), o)); + case UPB_DEF_FIELD: + return upb_fielddef_upcast_mutable( + upb_fielddef_dup(upb_downcast_fielddef(def), o)); + case UPB_DEF_ENUM: + return upb_enumdef_upcast_mutable( + upb_enumdef_dup(upb_downcast_enumdef(def), o)); + default: assert(false); return NULL; + } +} + +static bool upb_def_init(upb_def *def, upb_deftype_t type, + const struct upb_refcounted_vtbl *vtbl, + const void *owner) { + if (!upb_refcounted_init(upb_def_upcast_mutable(def), vtbl, owner)) return false; + def->type = type; + def->fullname = NULL; + def->came_from_user = false; + return true; +} + +static void upb_def_uninit(upb_def *def) { + free((void*)def->fullname); +} + +static const char *msgdef_name(const upb_msgdef *m) { + const char *name = upb_def_fullname(upb_msgdef_upcast(m)); + return name ? name : "(anonymous)"; +} + +static bool upb_validate_field(upb_fielddef *f, upb_status *s) { + if (upb_fielddef_name(f) == NULL || upb_fielddef_number(f) == 0) { + upb_status_seterrmsg(s, "fielddef must have name and number set"); + return false; + } + + if (!f->type_is_set_) { + upb_status_seterrmsg(s, "fielddef type was not initialized"); + return false; + } + + if (upb_fielddef_lazy(f) && + upb_fielddef_descriptortype(f) != UPB_DESCRIPTOR_TYPE_MESSAGE) { + upb_status_seterrmsg(s, + "only length-delimited submessage fields may be lazy"); + return false; + } + + if (upb_fielddef_hassubdef(f)) { + const upb_def *subdef; + + if (f->subdef_is_symbolic) { + upb_status_seterrf(s, "field '%s.%s' has not been resolved", + msgdef_name(f->msg.def), upb_fielddef_name(f)); + return false; + } + + subdef = upb_fielddef_subdef(f); + if (subdef == NULL) { + upb_status_seterrf(s, "field %s.%s is missing required subdef", + msgdef_name(f->msg.def), upb_fielddef_name(f)); + return false; + } + + if (!upb_def_isfrozen(subdef) && !subdef->came_from_user) { + upb_status_seterrf(s, + "subdef of field %s.%s is not frozen or being frozen", + msgdef_name(f->msg.def), upb_fielddef_name(f)); + return false; + } + } + + if (upb_fielddef_type(f) == UPB_TYPE_ENUM) { + bool has_default_name = upb_fielddef_enumhasdefaultstr(f); + bool has_default_number = upb_fielddef_enumhasdefaultint32(f); + + /* Previously verified by upb_validate_enumdef(). */ + assert(upb_enumdef_numvals(upb_fielddef_enumsubdef(f)) > 0); + + /* We've already validated that we have an associated enumdef and that it + * has at least one member, so at least one of these should be true. + * Because if the user didn't set anything, we'll pick up the enum's + * default, but if the user *did* set something we should at least pick up + * the one they set (int32 or string). */ + assert(has_default_name || has_default_number); + + if (!has_default_name) { + upb_status_seterrf(s, + "enum default for field %s.%s (%d) is not in the enum", + msgdef_name(f->msg.def), upb_fielddef_name(f), + upb_fielddef_defaultint32(f)); + return false; + } + + if (!has_default_number) { + upb_status_seterrf(s, + "enum default for field %s.%s (%s) is not in the enum", + msgdef_name(f->msg.def), upb_fielddef_name(f), + upb_fielddef_defaultstr(f, NULL)); + return false; + } + + /* Lift the effective numeric default into the field's default slot, in case + * we were only getting it "by reference" from the enumdef. */ + upb_fielddef_setdefaultint32(f, upb_fielddef_defaultint32(f)); + } + + /* Ensure that MapEntry submessages only appear as repeated fields, not + * optional/required (singular) fields. */ + if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE && + upb_fielddef_msgsubdef(f) != NULL) { + const upb_msgdef *subdef = upb_fielddef_msgsubdef(f); + if (upb_msgdef_mapentry(subdef) && !upb_fielddef_isseq(f)) { + upb_status_seterrf(s, + "Field %s refers to mapentry message but is not " + "a repeated field", + upb_fielddef_name(f) ? upb_fielddef_name(f) : + "(unnamed)"); + return false; + } + } + + return true; +} + +static bool upb_validate_enumdef(const upb_enumdef *e, upb_status *s) { + if (upb_enumdef_numvals(e) == 0) { + upb_status_seterrf(s, "enum %s has no members (must have at least one)", + upb_enumdef_fullname(e)); + return false; + } + + return true; +} + +/* All submessage fields are lower than all other fields. + * Secondly, fields are increasing in order. */ +uint32_t field_rank(const upb_fielddef *f) { + uint32_t ret = upb_fielddef_number(f); + const uint32_t high_bit = 1 << 30; + assert(ret < high_bit); + if (!upb_fielddef_issubmsg(f)) + ret |= high_bit; + return ret; +} + +int cmp_fields(const void *p1, const void *p2) { + const upb_fielddef *f1 = *(upb_fielddef*const*)p1; + const upb_fielddef *f2 = *(upb_fielddef*const*)p2; + return field_rank(f1) - field_rank(f2); +} + +static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { + /* Sort fields. upb internally relies on UPB_TYPE_MESSAGE fields having the + * lowest indexes, but we do not publicly guarantee this. */ + upb_msg_field_iter j; + int i; + uint32_t selector; + int n = upb_msgdef_numfields(m); + upb_fielddef **fields = malloc(n * sizeof(*fields)); + if (!fields) return false; + + m->submsg_field_count = 0; + for(i = 0, upb_msg_field_begin(&j, m); + !upb_msg_field_done(&j); + upb_msg_field_next(&j), i++) { + upb_fielddef *f = upb_msg_iter_field(&j); + assert(f->msg.def == m); + if (!upb_validate_field(f, s)) { + free(fields); + return false; + } + if (upb_fielddef_issubmsg(f)) { + m->submsg_field_count++; + } + fields[i] = f; + } + + qsort(fields, n, sizeof(*fields), cmp_fields); + + selector = UPB_STATIC_SELECTOR_COUNT + m->submsg_field_count; + for (i = 0; i < n; i++) { + upb_fielddef *f = fields[i]; + f->index_ = i; + f->selector_base = selector + upb_handlers_selectorbaseoffset(f); + selector += upb_handlers_selectorcount(f); + } + m->selector_count = selector; + +#ifndef NDEBUG + { + /* Verify that all selectors for the message are distinct. */ +#define TRY(type) \ + if (upb_handlers_getselector(f, type, &sel)) upb_inttable_insert(&t, sel, v); + + upb_inttable t; + upb_value v; + upb_selector_t sel; + + upb_inttable_init(&t, UPB_CTYPE_BOOL); + v = upb_value_bool(true); + upb_inttable_insert(&t, UPB_STARTMSG_SELECTOR, v); + upb_inttable_insert(&t, UPB_ENDMSG_SELECTOR, v); + for(upb_msg_field_begin(&j, m); + !upb_msg_field_done(&j); + upb_msg_field_next(&j)) { + upb_fielddef *f = upb_msg_iter_field(&j); + /* These calls will assert-fail in upb_table if the value already + * exists. */ + TRY(UPB_HANDLER_INT32); + TRY(UPB_HANDLER_INT64) + TRY(UPB_HANDLER_UINT32) + TRY(UPB_HANDLER_UINT64) + TRY(UPB_HANDLER_FLOAT) + TRY(UPB_HANDLER_DOUBLE) + TRY(UPB_HANDLER_BOOL) + TRY(UPB_HANDLER_STARTSTR) + TRY(UPB_HANDLER_STRING) + TRY(UPB_HANDLER_ENDSTR) + TRY(UPB_HANDLER_STARTSUBMSG) + TRY(UPB_HANDLER_ENDSUBMSG) + TRY(UPB_HANDLER_STARTSEQ) + TRY(UPB_HANDLER_ENDSEQ) + } + upb_inttable_uninit(&t); + } +#undef TRY +#endif + + free(fields); + return true; +} + +bool upb_def_freeze(upb_def *const* defs, int n, upb_status *s) { + int i; + int maxdepth; + bool ret; + upb_status_clear(s); + + /* First perform validation, in two passes so we can check that we have a + * transitive closure without needing to search. */ + for (i = 0; i < n; i++) { + upb_def *def = defs[i]; + if (upb_def_isfrozen(def)) { + /* Could relax this requirement if it's annoying. */ + upb_status_seterrmsg(s, "def is already frozen"); + goto err; + } else if (def->type == UPB_DEF_FIELD) { + upb_status_seterrmsg(s, "standalone fielddefs can not be frozen"); + goto err; + } else if (def->type == UPB_DEF_ENUM) { + if (!upb_validate_enumdef(upb_dyncast_enumdef(def), s)) { + goto err; + } + } else { + /* Set now to detect transitive closure in the second pass. */ + def->came_from_user = true; + } + } + + /* Second pass of validation. Also assign selector bases and indexes, and + * compact tables. */ + for (i = 0; i < n; i++) { + upb_msgdef *m = upb_dyncast_msgdef_mutable(defs[i]); + upb_enumdef *e = upb_dyncast_enumdef_mutable(defs[i]); + if (m) { + upb_inttable_compact(&m->itof); + if (!assign_msg_indices(m, s)) { + goto err; + } + } else if (e) { + upb_inttable_compact(&e->iton); + } + } + + /* Def graph contains FieldDefs between each MessageDef, so double the + * limit. */ + maxdepth = UPB_MAX_MESSAGE_DEPTH * 2; + + /* Validation all passed; freeze the defs. */ + ret = upb_refcounted_freeze((upb_refcounted * const *)defs, n, s, maxdepth); + assert(!(s && ret != upb_ok(s))); + return ret; + +err: + for (i = 0; i < n; i++) { + defs[i]->came_from_user = false; + } + assert(!(s && upb_ok(s))); + return false; +} + + +/* upb_enumdef ****************************************************************/ + +static void upb_enumdef_free(upb_refcounted *r) { + upb_enumdef *e = (upb_enumdef*)r; + upb_inttable_iter i; + upb_inttable_begin(&i, &e->iton); + for( ; !upb_inttable_done(&i); upb_inttable_next(&i)) { + /* To clean up the upb_strdup() from upb_enumdef_addval(). */ + free(upb_value_getcstr(upb_inttable_iter_value(&i))); + } + upb_strtable_uninit(&e->ntoi); + upb_inttable_uninit(&e->iton); + upb_def_uninit(upb_enumdef_upcast_mutable(e)); + free(e); +} + +upb_enumdef *upb_enumdef_new(const void *owner) { + static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_enumdef_free}; + upb_enumdef *e = malloc(sizeof(*e)); + if (!e) return NULL; + if (!upb_def_init(upb_enumdef_upcast_mutable(e), UPB_DEF_ENUM, &vtbl, owner)) + goto err2; + if (!upb_strtable_init(&e->ntoi, UPB_CTYPE_INT32)) goto err2; + if (!upb_inttable_init(&e->iton, UPB_CTYPE_CSTR)) goto err1; + return e; + +err1: + upb_strtable_uninit(&e->ntoi); +err2: + free(e); + return NULL; +} + +upb_enumdef *upb_enumdef_dup(const upb_enumdef *e, const void *owner) { + upb_enum_iter i; + upb_enumdef *new_e = upb_enumdef_new(owner); + if (!new_e) return NULL; + for(upb_enum_begin(&i, e); !upb_enum_done(&i); upb_enum_next(&i)) { + bool success = upb_enumdef_addval( + new_e, upb_enum_iter_name(&i),upb_enum_iter_number(&i), NULL); + if (!success) { + upb_enumdef_unref(new_e, owner); + return NULL; + } + } + return new_e; +} + +bool upb_enumdef_freeze(upb_enumdef *e, upb_status *status) { + upb_def *d = upb_enumdef_upcast_mutable(e); + return upb_def_freeze(&d, 1, status); +} + +const char *upb_enumdef_fullname(const upb_enumdef *e) { + return upb_def_fullname(upb_enumdef_upcast(e)); +} + +bool upb_enumdef_setfullname(upb_enumdef *e, const char *fullname, + upb_status *s) { + return upb_def_setfullname(upb_enumdef_upcast_mutable(e), fullname, s); +} + +bool upb_enumdef_addval(upb_enumdef *e, const char *name, int32_t num, + upb_status *status) { + if (!upb_isident(name, strlen(name), false, status)) { + return false; + } + if (upb_enumdef_ntoiz(e, name, NULL)) { + upb_status_seterrf(status, "name '%s' is already defined", name); + return false; + } + if (!upb_strtable_insert(&e->ntoi, name, upb_value_int32(num))) { + upb_status_seterrmsg(status, "out of memory"); + return false; + } + if (!upb_inttable_lookup(&e->iton, num, NULL) && + !upb_inttable_insert(&e->iton, num, upb_value_cstr(upb_strdup(name)))) { + upb_status_seterrmsg(status, "out of memory"); + upb_strtable_remove(&e->ntoi, name, NULL); + return false; + } + if (upb_enumdef_numvals(e) == 1) { + bool ok = upb_enumdef_setdefault(e, num, NULL); + UPB_ASSERT_VAR(ok, ok); + } + return true; +} + +int32_t upb_enumdef_default(const upb_enumdef *e) { + assert(upb_enumdef_iton(e, e->defaultval)); + return e->defaultval; +} + +bool upb_enumdef_setdefault(upb_enumdef *e, int32_t val, upb_status *s) { + assert(!upb_enumdef_isfrozen(e)); + if (!upb_enumdef_iton(e, val)) { + upb_status_seterrf(s, "number '%d' is not in the enum.", val); + return false; + } + e->defaultval = val; + return true; +} + +int upb_enumdef_numvals(const upb_enumdef *e) { + return upb_strtable_count(&e->ntoi); +} + +void upb_enum_begin(upb_enum_iter *i, const upb_enumdef *e) { + /* We iterate over the ntoi table, to account for duplicate numbers. */ + upb_strtable_begin(i, &e->ntoi); +} + +void upb_enum_next(upb_enum_iter *iter) { upb_strtable_next(iter); } +bool upb_enum_done(upb_enum_iter *iter) { return upb_strtable_done(iter); } + +bool upb_enumdef_ntoi(const upb_enumdef *def, const char *name, + size_t len, int32_t *num) { + upb_value v; + if (!upb_strtable_lookup2(&def->ntoi, name, len, &v)) { + return false; + } + if (num) *num = upb_value_getint32(v); + return true; +} + +const char *upb_enumdef_iton(const upb_enumdef *def, int32_t num) { + upb_value v; + return upb_inttable_lookup32(&def->iton, num, &v) ? + upb_value_getcstr(v) : NULL; +} + +const char *upb_enum_iter_name(upb_enum_iter *iter) { + return upb_strtable_iter_key(iter); +} + +int32_t upb_enum_iter_number(upb_enum_iter *iter) { + return upb_value_getint32(upb_strtable_iter_value(iter)); +} + + +/* upb_fielddef ***************************************************************/ + +static void upb_fielddef_init_default(upb_fielddef *f); + +static void upb_fielddef_uninit_default(upb_fielddef *f) { + if (f->type_is_set_ && f->default_is_string && f->defaultval.bytes) + freestr(f->defaultval.bytes); +} + +static void visitfield(const upb_refcounted *r, upb_refcounted_visit *visit, + void *closure) { + const upb_fielddef *f = (const upb_fielddef*)r; + if (upb_fielddef_containingtype(f)) { + visit(r, upb_msgdef_upcast2(upb_fielddef_containingtype(f)), closure); + } + if (upb_fielddef_containingoneof(f)) { + visit(r, upb_oneofdef_upcast2(upb_fielddef_containingoneof(f)), closure); + } + if (upb_fielddef_subdef(f)) { + visit(r, upb_def_upcast(upb_fielddef_subdef(f)), closure); + } +} + +static void freefield(upb_refcounted *r) { + upb_fielddef *f = (upb_fielddef*)r; + upb_fielddef_uninit_default(f); + if (f->subdef_is_symbolic) + free(f->sub.name); + upb_def_uninit(upb_fielddef_upcast_mutable(f)); + free(f); +} + +static const char *enumdefaultstr(const upb_fielddef *f) { + const upb_enumdef *e; + assert(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM); + e = upb_fielddef_enumsubdef(f); + if (f->default_is_string && f->defaultval.bytes) { + /* Default was explicitly set as a string. */ + str_t *s = f->defaultval.bytes; + return s->str; + } else if (e) { + if (!f->default_is_string) { + /* Default was explicitly set as an integer; look it up in enumdef. */ + const char *name = upb_enumdef_iton(e, f->defaultval.sint); + if (name) { + return name; + } + } else { + /* Default is completely unset; pull enumdef default. */ + if (upb_enumdef_numvals(e) > 0) { + const char *name = upb_enumdef_iton(e, upb_enumdef_default(e)); + assert(name); + return name; + } + } + } + return NULL; +} + +static bool enumdefaultint32(const upb_fielddef *f, int32_t *val) { + const upb_enumdef *e; + assert(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM); + e = upb_fielddef_enumsubdef(f); + if (!f->default_is_string) { + /* Default was explicitly set as an integer. */ + *val = f->defaultval.sint; + return true; + } else if (e) { + if (f->defaultval.bytes) { + /* Default was explicitly set as a str; try to lookup corresponding int. */ + str_t *s = f->defaultval.bytes; + if (upb_enumdef_ntoiz(e, s->str, val)) { + return true; + } + } else { + /* Default is unset; try to pull in enumdef default. */ + if (upb_enumdef_numvals(e) > 0) { + *val = upb_enumdef_default(e); + return true; + } + } + } + return false; +} + +upb_fielddef *upb_fielddef_new(const void *o) { + static const struct upb_refcounted_vtbl vtbl = {visitfield, freefield}; + upb_fielddef *f = malloc(sizeof(*f)); + if (!f) return NULL; + if (!upb_def_init(upb_fielddef_upcast_mutable(f), UPB_DEF_FIELD, &vtbl, o)) { + free(f); + return NULL; + } + f->msg.def = NULL; + f->sub.def = NULL; + f->oneof = NULL; + f->subdef_is_symbolic = false; + f->msg_is_symbolic = false; + f->label_ = UPB_LABEL_OPTIONAL; + f->type_ = UPB_TYPE_INT32; + f->number_ = 0; + f->type_is_set_ = false; + f->tagdelim = false; + f->is_extension_ = false; + f->lazy_ = false; + f->packed_ = true; + + /* For the moment we default this to UPB_INTFMT_VARIABLE, since it will work + * with all integer types and is in some since more "default" since the most + * normal-looking proto2 types int32/int64/uint32/uint64 use variable. + * + * Other options to consider: + * - there is no default; users must set this manually (like type). + * - default signed integers to UPB_INTFMT_ZIGZAG, since it's more likely to + * be an optimal default for signed integers. */ + f->intfmt = UPB_INTFMT_VARIABLE; + return f; +} + +upb_fielddef *upb_fielddef_dup(const upb_fielddef *f, const void *owner) { + const char *srcname; + upb_fielddef *newf = upb_fielddef_new(owner); + if (!newf) return NULL; + upb_fielddef_settype(newf, upb_fielddef_type(f)); + upb_fielddef_setlabel(newf, upb_fielddef_label(f)); + upb_fielddef_setnumber(newf, upb_fielddef_number(f), NULL); + upb_fielddef_setname(newf, upb_fielddef_name(f), NULL); + if (f->default_is_string && f->defaultval.bytes) { + str_t *s = f->defaultval.bytes; + upb_fielddef_setdefaultstr(newf, s->str, s->len, NULL); + } else { + newf->default_is_string = f->default_is_string; + newf->defaultval = f->defaultval; + } + + if (f->subdef_is_symbolic) { + srcname = f->sub.name; /* Might be NULL. */ + } else { + srcname = f->sub.def ? upb_def_fullname(f->sub.def) : NULL; + } + if (srcname) { + char *newname = malloc(strlen(f->sub.def->fullname) + 2); + if (!newname) { + upb_fielddef_unref(newf, owner); + return NULL; + } + strcpy(newname, "."); + strcat(newname, f->sub.def->fullname); + upb_fielddef_setsubdefname(newf, newname, NULL); + free(newname); + } + + return newf; +} + +bool upb_fielddef_typeisset(const upb_fielddef *f) { + return f->type_is_set_; +} + +upb_fieldtype_t upb_fielddef_type(const upb_fielddef *f) { + assert(f->type_is_set_); + return f->type_; +} + +uint32_t upb_fielddef_index(const upb_fielddef *f) { + return f->index_; +} + +upb_label_t upb_fielddef_label(const upb_fielddef *f) { + return f->label_; +} + +upb_intfmt_t upb_fielddef_intfmt(const upb_fielddef *f) { + return f->intfmt; +} + +bool upb_fielddef_istagdelim(const upb_fielddef *f) { + return f->tagdelim; +} + +uint32_t upb_fielddef_number(const upb_fielddef *f) { + return f->number_; +} + +bool upb_fielddef_isextension(const upb_fielddef *f) { + return f->is_extension_; +} + +bool upb_fielddef_lazy(const upb_fielddef *f) { + return f->lazy_; +} + +bool upb_fielddef_packed(const upb_fielddef *f) { + return f->packed_; +} + +const char *upb_fielddef_name(const upb_fielddef *f) { + return upb_def_fullname(upb_fielddef_upcast(f)); +} + +const upb_msgdef *upb_fielddef_containingtype(const upb_fielddef *f) { + return f->msg_is_symbolic ? NULL : f->msg.def; +} + +const upb_oneofdef *upb_fielddef_containingoneof(const upb_fielddef *f) { + return f->oneof; +} + +upb_msgdef *upb_fielddef_containingtype_mutable(upb_fielddef *f) { + return (upb_msgdef*)upb_fielddef_containingtype(f); +} + +const char *upb_fielddef_containingtypename(upb_fielddef *f) { + return f->msg_is_symbolic ? f->msg.name : NULL; +} + +static void release_containingtype(upb_fielddef *f) { + if (f->msg_is_symbolic) free(f->msg.name); +} + +bool upb_fielddef_setcontainingtypename(upb_fielddef *f, const char *name, + upb_status *s) { + assert(!upb_fielddef_isfrozen(f)); + if (upb_fielddef_containingtype(f)) { + upb_status_seterrmsg(s, "field has already been added to a message."); + return false; + } + /* TODO: validate name (upb_isident() doesn't quite work atm because this name + * may have a leading "."). */ + release_containingtype(f); + f->msg.name = upb_strdup(name); + f->msg_is_symbolic = true; + return true; +} + +bool upb_fielddef_setname(upb_fielddef *f, const char *name, upb_status *s) { + if (upb_fielddef_containingtype(f) || upb_fielddef_containingoneof(f)) { + upb_status_seterrmsg(s, "Already added to message or oneof"); + return false; + } + return upb_def_setfullname(upb_fielddef_upcast_mutable(f), name, s); +} + +static void chkdefaulttype(const upb_fielddef *f, upb_fieldtype_t type) { + UPB_UNUSED(f); + UPB_UNUSED(type); + assert(f->type_is_set_ && upb_fielddef_type(f) == type); +} + +int64_t upb_fielddef_defaultint64(const upb_fielddef *f) { + chkdefaulttype(f, UPB_TYPE_INT64); + return f->defaultval.sint; +} + +int32_t upb_fielddef_defaultint32(const upb_fielddef *f) { + if (f->type_is_set_ && upb_fielddef_type(f) == UPB_TYPE_ENUM) { + int32_t val; + bool ok = enumdefaultint32(f, &val); + UPB_ASSERT_VAR(ok, ok); + return val; + } else { + chkdefaulttype(f, UPB_TYPE_INT32); + return f->defaultval.sint; + } +} + +uint64_t upb_fielddef_defaultuint64(const upb_fielddef *f) { + chkdefaulttype(f, UPB_TYPE_UINT64); + return f->defaultval.uint; +} + +uint32_t upb_fielddef_defaultuint32(const upb_fielddef *f) { + chkdefaulttype(f, UPB_TYPE_UINT32); + return f->defaultval.uint; +} + +bool upb_fielddef_defaultbool(const upb_fielddef *f) { + chkdefaulttype(f, UPB_TYPE_BOOL); + return f->defaultval.uint; +} + +float upb_fielddef_defaultfloat(const upb_fielddef *f) { + chkdefaulttype(f, UPB_TYPE_FLOAT); + return f->defaultval.flt; +} + +double upb_fielddef_defaultdouble(const upb_fielddef *f) { + chkdefaulttype(f, UPB_TYPE_DOUBLE); + return f->defaultval.dbl; +} + +const char *upb_fielddef_defaultstr(const upb_fielddef *f, size_t *len) { + assert(f->type_is_set_); + assert(upb_fielddef_type(f) == UPB_TYPE_STRING || + upb_fielddef_type(f) == UPB_TYPE_BYTES || + upb_fielddef_type(f) == UPB_TYPE_ENUM); + + if (upb_fielddef_type(f) == UPB_TYPE_ENUM) { + const char *ret = enumdefaultstr(f); + assert(ret); + /* Enum defaults can't have embedded NULLs. */ + if (len) *len = strlen(ret); + return ret; + } + + if (f->default_is_string) { + str_t *str = f->defaultval.bytes; + if (len) *len = str->len; + return str->str; + } + + return NULL; +} + +static void upb_fielddef_init_default(upb_fielddef *f) { + f->default_is_string = false; + switch (upb_fielddef_type(f)) { + case UPB_TYPE_DOUBLE: f->defaultval.dbl = 0; break; + case UPB_TYPE_FLOAT: f->defaultval.flt = 0; break; + case UPB_TYPE_INT32: + case UPB_TYPE_INT64: f->defaultval.sint = 0; break; + case UPB_TYPE_UINT64: + case UPB_TYPE_UINT32: + case UPB_TYPE_BOOL: f->defaultval.uint = 0; break; + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: + f->defaultval.bytes = newstr("", 0); + f->default_is_string = true; + break; + case UPB_TYPE_MESSAGE: break; + case UPB_TYPE_ENUM: + /* This is our special sentinel that indicates "not set" for an enum. */ + f->default_is_string = true; + f->defaultval.bytes = NULL; + break; + } +} + +const upb_def *upb_fielddef_subdef(const upb_fielddef *f) { + return f->subdef_is_symbolic ? NULL : f->sub.def; +} + +const upb_msgdef *upb_fielddef_msgsubdef(const upb_fielddef *f) { + const upb_def *def = upb_fielddef_subdef(f); + return def ? upb_dyncast_msgdef(def) : NULL; +} + +const upb_enumdef *upb_fielddef_enumsubdef(const upb_fielddef *f) { + const upb_def *def = upb_fielddef_subdef(f); + return def ? upb_dyncast_enumdef(def) : NULL; +} + +upb_def *upb_fielddef_subdef_mutable(upb_fielddef *f) { + return (upb_def*)upb_fielddef_subdef(f); +} + +const char *upb_fielddef_subdefname(const upb_fielddef *f) { + if (f->subdef_is_symbolic) { + return f->sub.name; + } else if (f->sub.def) { + return upb_def_fullname(f->sub.def); + } else { + return NULL; + } +} + +bool upb_fielddef_setnumber(upb_fielddef *f, uint32_t number, upb_status *s) { + if (upb_fielddef_containingtype(f)) { + upb_status_seterrmsg( + s, "cannot change field number after adding to a message"); + return false; + } + if (number == 0 || number > UPB_MAX_FIELDNUMBER) { + upb_status_seterrf(s, "invalid field number (%u)", number); + return false; + } + f->number_ = number; + return true; +} + +void upb_fielddef_settype(upb_fielddef *f, upb_fieldtype_t type) { + assert(!upb_fielddef_isfrozen(f)); + assert(upb_fielddef_checktype(type)); + upb_fielddef_uninit_default(f); + f->type_ = type; + f->type_is_set_ = true; + upb_fielddef_init_default(f); +} + +void upb_fielddef_setdescriptortype(upb_fielddef *f, int type) { + assert(!upb_fielddef_isfrozen(f)); + switch (type) { + case UPB_DESCRIPTOR_TYPE_DOUBLE: + upb_fielddef_settype(f, UPB_TYPE_DOUBLE); + break; + case UPB_DESCRIPTOR_TYPE_FLOAT: + upb_fielddef_settype(f, UPB_TYPE_FLOAT); + break; + case UPB_DESCRIPTOR_TYPE_INT64: + case UPB_DESCRIPTOR_TYPE_SFIXED64: + case UPB_DESCRIPTOR_TYPE_SINT64: + upb_fielddef_settype(f, UPB_TYPE_INT64); + break; + case UPB_DESCRIPTOR_TYPE_UINT64: + case UPB_DESCRIPTOR_TYPE_FIXED64: + upb_fielddef_settype(f, UPB_TYPE_UINT64); + break; + case UPB_DESCRIPTOR_TYPE_INT32: + case UPB_DESCRIPTOR_TYPE_SFIXED32: + case UPB_DESCRIPTOR_TYPE_SINT32: + upb_fielddef_settype(f, UPB_TYPE_INT32); + break; + case UPB_DESCRIPTOR_TYPE_UINT32: + case UPB_DESCRIPTOR_TYPE_FIXED32: + upb_fielddef_settype(f, UPB_TYPE_UINT32); + break; + case UPB_DESCRIPTOR_TYPE_BOOL: + upb_fielddef_settype(f, UPB_TYPE_BOOL); + break; + case UPB_DESCRIPTOR_TYPE_STRING: + upb_fielddef_settype(f, UPB_TYPE_STRING); + break; + case UPB_DESCRIPTOR_TYPE_BYTES: + upb_fielddef_settype(f, UPB_TYPE_BYTES); + break; + case UPB_DESCRIPTOR_TYPE_GROUP: + case UPB_DESCRIPTOR_TYPE_MESSAGE: + upb_fielddef_settype(f, UPB_TYPE_MESSAGE); + break; + case UPB_DESCRIPTOR_TYPE_ENUM: + upb_fielddef_settype(f, UPB_TYPE_ENUM); + break; + default: assert(false); + } + + if (type == UPB_DESCRIPTOR_TYPE_FIXED64 || + type == UPB_DESCRIPTOR_TYPE_FIXED32 || + type == UPB_DESCRIPTOR_TYPE_SFIXED64 || + type == UPB_DESCRIPTOR_TYPE_SFIXED32) { + upb_fielddef_setintfmt(f, UPB_INTFMT_FIXED); + } else if (type == UPB_DESCRIPTOR_TYPE_SINT64 || + type == UPB_DESCRIPTOR_TYPE_SINT32) { + upb_fielddef_setintfmt(f, UPB_INTFMT_ZIGZAG); + } else { + upb_fielddef_setintfmt(f, UPB_INTFMT_VARIABLE); + } + + upb_fielddef_settagdelim(f, type == UPB_DESCRIPTOR_TYPE_GROUP); +} + +upb_descriptortype_t upb_fielddef_descriptortype(const upb_fielddef *f) { + switch (upb_fielddef_type(f)) { + case UPB_TYPE_FLOAT: return UPB_DESCRIPTOR_TYPE_FLOAT; + case UPB_TYPE_DOUBLE: return UPB_DESCRIPTOR_TYPE_DOUBLE; + case UPB_TYPE_BOOL: return UPB_DESCRIPTOR_TYPE_BOOL; + case UPB_TYPE_STRING: return UPB_DESCRIPTOR_TYPE_STRING; + case UPB_TYPE_BYTES: return UPB_DESCRIPTOR_TYPE_BYTES; + case UPB_TYPE_ENUM: return UPB_DESCRIPTOR_TYPE_ENUM; + case UPB_TYPE_INT32: + switch (upb_fielddef_intfmt(f)) { + case UPB_INTFMT_VARIABLE: return UPB_DESCRIPTOR_TYPE_INT32; + case UPB_INTFMT_FIXED: return UPB_DESCRIPTOR_TYPE_SFIXED32; + case UPB_INTFMT_ZIGZAG: return UPB_DESCRIPTOR_TYPE_SINT32; + } + case UPB_TYPE_INT64: + switch (upb_fielddef_intfmt(f)) { + case UPB_INTFMT_VARIABLE: return UPB_DESCRIPTOR_TYPE_INT64; + case UPB_INTFMT_FIXED: return UPB_DESCRIPTOR_TYPE_SFIXED64; + case UPB_INTFMT_ZIGZAG: return UPB_DESCRIPTOR_TYPE_SINT64; + } + case UPB_TYPE_UINT32: + switch (upb_fielddef_intfmt(f)) { + case UPB_INTFMT_VARIABLE: return UPB_DESCRIPTOR_TYPE_UINT32; + case UPB_INTFMT_FIXED: return UPB_DESCRIPTOR_TYPE_FIXED32; + case UPB_INTFMT_ZIGZAG: return -1; + } + case UPB_TYPE_UINT64: + switch (upb_fielddef_intfmt(f)) { + case UPB_INTFMT_VARIABLE: return UPB_DESCRIPTOR_TYPE_UINT64; + case UPB_INTFMT_FIXED: return UPB_DESCRIPTOR_TYPE_FIXED64; + case UPB_INTFMT_ZIGZAG: return -1; + } + case UPB_TYPE_MESSAGE: + return upb_fielddef_istagdelim(f) ? + UPB_DESCRIPTOR_TYPE_GROUP : UPB_DESCRIPTOR_TYPE_MESSAGE; + } + return 0; +} + +void upb_fielddef_setisextension(upb_fielddef *f, bool is_extension) { + assert(!upb_fielddef_isfrozen(f)); + f->is_extension_ = is_extension; +} + +void upb_fielddef_setlazy(upb_fielddef *f, bool lazy) { + assert(!upb_fielddef_isfrozen(f)); + f->lazy_ = lazy; +} + +void upb_fielddef_setpacked(upb_fielddef *f, bool packed) { + assert(!upb_fielddef_isfrozen(f)); + f->packed_ = packed; +} + +void upb_fielddef_setlabel(upb_fielddef *f, upb_label_t label) { + assert(!upb_fielddef_isfrozen(f)); + assert(upb_fielddef_checklabel(label)); + f->label_ = label; +} + +void upb_fielddef_setintfmt(upb_fielddef *f, upb_intfmt_t fmt) { + assert(!upb_fielddef_isfrozen(f)); + assert(upb_fielddef_checkintfmt(fmt)); + f->intfmt = fmt; +} + +void upb_fielddef_settagdelim(upb_fielddef *f, bool tag_delim) { + assert(!upb_fielddef_isfrozen(f)); + f->tagdelim = tag_delim; + f->tagdelim = tag_delim; +} + +static bool checksetdefault(upb_fielddef *f, upb_fieldtype_t type) { + if (!f->type_is_set_ || upb_fielddef_isfrozen(f) || + upb_fielddef_type(f) != type) { + assert(false); + return false; + } + if (f->default_is_string) { + str_t *s = f->defaultval.bytes; + assert(s || type == UPB_TYPE_ENUM); + if (s) freestr(s); + } + f->default_is_string = false; + return true; +} + +void upb_fielddef_setdefaultint64(upb_fielddef *f, int64_t value) { + if (checksetdefault(f, UPB_TYPE_INT64)) + f->defaultval.sint = value; +} + +void upb_fielddef_setdefaultint32(upb_fielddef *f, int32_t value) { + if ((upb_fielddef_type(f) == UPB_TYPE_ENUM && + checksetdefault(f, UPB_TYPE_ENUM)) || + checksetdefault(f, UPB_TYPE_INT32)) { + f->defaultval.sint = value; + } +} + +void upb_fielddef_setdefaultuint64(upb_fielddef *f, uint64_t value) { + if (checksetdefault(f, UPB_TYPE_UINT64)) + f->defaultval.uint = value; +} + +void upb_fielddef_setdefaultuint32(upb_fielddef *f, uint32_t value) { + if (checksetdefault(f, UPB_TYPE_UINT32)) + f->defaultval.uint = value; +} + +void upb_fielddef_setdefaultbool(upb_fielddef *f, bool value) { + if (checksetdefault(f, UPB_TYPE_BOOL)) + f->defaultval.uint = value; +} + +void upb_fielddef_setdefaultfloat(upb_fielddef *f, float value) { + if (checksetdefault(f, UPB_TYPE_FLOAT)) + f->defaultval.flt = value; +} + +void upb_fielddef_setdefaultdouble(upb_fielddef *f, double value) { + if (checksetdefault(f, UPB_TYPE_DOUBLE)) + f->defaultval.dbl = value; +} + +bool upb_fielddef_setdefaultstr(upb_fielddef *f, const void *str, size_t len, + upb_status *s) { + str_t *str2; + assert(upb_fielddef_isstring(f) || f->type_ == UPB_TYPE_ENUM); + if (f->type_ == UPB_TYPE_ENUM && !upb_isident(str, len, false, s)) + return false; + + if (f->default_is_string) { + str_t *s = f->defaultval.bytes; + assert(s || f->type_ == UPB_TYPE_ENUM); + if (s) freestr(s); + } else { + assert(f->type_ == UPB_TYPE_ENUM); + } + + str2 = newstr(str, len); + f->defaultval.bytes = str2; + f->default_is_string = true; + return true; +} + +void upb_fielddef_setdefaultcstr(upb_fielddef *f, const char *str, + upb_status *s) { + assert(f->type_is_set_); + upb_fielddef_setdefaultstr(f, str, str ? strlen(str) : 0, s); +} + +bool upb_fielddef_enumhasdefaultint32(const upb_fielddef *f) { + int32_t val; + assert(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM); + return enumdefaultint32(f, &val); +} + +bool upb_fielddef_enumhasdefaultstr(const upb_fielddef *f) { + assert(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM); + return enumdefaultstr(f) != NULL; +} + +static bool upb_subdef_typecheck(upb_fielddef *f, const upb_def *subdef, + upb_status *s) { + if (f->type_ == UPB_TYPE_MESSAGE) { + if (upb_dyncast_msgdef(subdef)) return true; + upb_status_seterrmsg(s, "invalid subdef type for this submessage field"); + return false; + } else if (f->type_ == UPB_TYPE_ENUM) { + if (upb_dyncast_enumdef(subdef)) return true; + upb_status_seterrmsg(s, "invalid subdef type for this enum field"); + return false; + } else { + upb_status_seterrmsg(s, "only message and enum fields can have a subdef"); + return false; + } +} + +static void release_subdef(upb_fielddef *f) { + if (f->subdef_is_symbolic) { + free(f->sub.name); + } else if (f->sub.def) { + upb_unref2(f->sub.def, f); + } +} + +bool upb_fielddef_setsubdef(upb_fielddef *f, const upb_def *subdef, + upb_status *s) { + assert(!upb_fielddef_isfrozen(f)); + assert(upb_fielddef_hassubdef(f)); + if (subdef && !upb_subdef_typecheck(f, subdef, s)) return false; + release_subdef(f); + f->sub.def = subdef; + f->subdef_is_symbolic = false; + if (f->sub.def) upb_ref2(f->sub.def, f); + return true; +} + +bool upb_fielddef_setmsgsubdef(upb_fielddef *f, const upb_msgdef *subdef, + upb_status *s) { + return upb_fielddef_setsubdef(f, upb_msgdef_upcast(subdef), s); +} + +bool upb_fielddef_setenumsubdef(upb_fielddef *f, const upb_enumdef *subdef, + upb_status *s) { + return upb_fielddef_setsubdef(f, upb_enumdef_upcast(subdef), s); +} + +bool upb_fielddef_setsubdefname(upb_fielddef *f, const char *name, + upb_status *s) { + assert(!upb_fielddef_isfrozen(f)); + if (!upb_fielddef_hassubdef(f)) { + upb_status_seterrmsg(s, "field type does not accept a subdef"); + return false; + } + /* TODO: validate name (upb_isident() doesn't quite work atm because this name + * may have a leading "."). */ + release_subdef(f); + f->sub.name = upb_strdup(name); + f->subdef_is_symbolic = true; + return true; +} + +bool upb_fielddef_issubmsg(const upb_fielddef *f) { + return upb_fielddef_type(f) == UPB_TYPE_MESSAGE; +} + +bool upb_fielddef_isstring(const upb_fielddef *f) { + return upb_fielddef_type(f) == UPB_TYPE_STRING || + upb_fielddef_type(f) == UPB_TYPE_BYTES; +} + +bool upb_fielddef_isseq(const upb_fielddef *f) { + return upb_fielddef_label(f) == UPB_LABEL_REPEATED; +} + +bool upb_fielddef_isprimitive(const upb_fielddef *f) { + return !upb_fielddef_isstring(f) && !upb_fielddef_issubmsg(f); +} + +bool upb_fielddef_ismap(const upb_fielddef *f) { + return upb_fielddef_isseq(f) && upb_fielddef_issubmsg(f) && + upb_msgdef_mapentry(upb_fielddef_msgsubdef(f)); +} + +bool upb_fielddef_hassubdef(const upb_fielddef *f) { + return upb_fielddef_issubmsg(f) || upb_fielddef_type(f) == UPB_TYPE_ENUM; +} + +static bool between(int32_t x, int32_t low, int32_t high) { + return x >= low && x <= high; +} + +bool upb_fielddef_checklabel(int32_t label) { return between(label, 1, 3); } +bool upb_fielddef_checktype(int32_t type) { return between(type, 1, 11); } +bool upb_fielddef_checkintfmt(int32_t fmt) { return between(fmt, 1, 3); } + +bool upb_fielddef_checkdescriptortype(int32_t type) { + return between(type, 1, 18); +} + +/* upb_msgdef *****************************************************************/ + +static void visitmsg(const upb_refcounted *r, upb_refcounted_visit *visit, + void *closure) { + upb_msg_oneof_iter o; + const upb_msgdef *m = (const upb_msgdef*)r; + upb_msg_field_iter i; + for(upb_msg_field_begin(&i, m); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { + upb_fielddef *f = upb_msg_iter_field(&i); + visit(r, upb_fielddef_upcast2(f), closure); + } + for(upb_msg_oneof_begin(&o, m); + !upb_msg_oneof_done(&o); + upb_msg_oneof_next(&o)) { + upb_oneofdef *f = upb_msg_iter_oneof(&o); + visit(r, upb_oneofdef_upcast2(f), closure); + } +} + +static void freemsg(upb_refcounted *r) { + upb_msgdef *m = (upb_msgdef*)r; + upb_strtable_uninit(&m->ntoo); + upb_strtable_uninit(&m->ntof); + upb_inttable_uninit(&m->itof); + upb_def_uninit(upb_msgdef_upcast_mutable(m)); + free(m); +} + +upb_msgdef *upb_msgdef_new(const void *owner) { + static const struct upb_refcounted_vtbl vtbl = {visitmsg, freemsg}; + upb_msgdef *m = malloc(sizeof(*m)); + if (!m) return NULL; + if (!upb_def_init(upb_msgdef_upcast_mutable(m), UPB_DEF_MSG, &vtbl, owner)) + goto err2; + if (!upb_inttable_init(&m->itof, UPB_CTYPE_PTR)) goto err3; + if (!upb_strtable_init(&m->ntof, UPB_CTYPE_PTR)) goto err2; + if (!upb_strtable_init(&m->ntoo, UPB_CTYPE_PTR)) goto err1; + m->map_entry = false; + return m; + +err1: + upb_strtable_uninit(&m->ntof); +err2: + upb_inttable_uninit(&m->itof); +err3: + free(m); + return NULL; +} + +upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner) { + bool ok; + upb_msg_field_iter i; + upb_msg_oneof_iter o; + + upb_msgdef *newm = upb_msgdef_new(owner); + if (!newm) return NULL; + ok = upb_def_setfullname(upb_msgdef_upcast_mutable(newm), + upb_def_fullname(upb_msgdef_upcast(m)), + NULL); + newm->map_entry = m->map_entry; + UPB_ASSERT_VAR(ok, ok); + for(upb_msg_field_begin(&i, m); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { + upb_fielddef *f = upb_fielddef_dup(upb_msg_iter_field(&i), &f); + /* Fields in oneofs are dup'd below. */ + if (upb_fielddef_containingoneof(f)) continue; + if (!f || !upb_msgdef_addfield(newm, f, &f, NULL)) { + upb_msgdef_unref(newm, owner); + return NULL; + } + } + for(upb_msg_oneof_begin(&o, m); + !upb_msg_oneof_done(&o); + upb_msg_oneof_next(&o)) { + upb_oneofdef *f = upb_oneofdef_dup(upb_msg_iter_oneof(&o), &f); + if (!f || !upb_msgdef_addoneof(newm, f, &f, NULL)) { + upb_msgdef_unref(newm, owner); + return NULL; + } + } + return newm; +} + +bool upb_msgdef_freeze(upb_msgdef *m, upb_status *status) { + upb_def *d = upb_msgdef_upcast_mutable(m); + return upb_def_freeze(&d, 1, status); +} + +const char *upb_msgdef_fullname(const upb_msgdef *m) { + return upb_def_fullname(upb_msgdef_upcast(m)); +} + +bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, + upb_status *s) { + return upb_def_setfullname(upb_msgdef_upcast_mutable(m), fullname, s); +} + +/* Helper: check that the field |f| is safe to add to msgdef |m|. Set an error + * on status |s| and return false if not. */ +static bool check_field_add(const upb_msgdef *m, const upb_fielddef *f, + upb_status *s) { + if (upb_fielddef_containingtype(f) != NULL) { + upb_status_seterrmsg(s, "fielddef already belongs to a message"); + return false; + } else if (upb_fielddef_name(f) == NULL || upb_fielddef_number(f) == 0) { + upb_status_seterrmsg(s, "field name or number were not set"); + return false; + } else if (upb_msgdef_ntofz(m, upb_fielddef_name(f)) || + upb_msgdef_itof(m, upb_fielddef_number(f))) { + upb_status_seterrmsg(s, "duplicate field name or number for field"); + return false; + } + return true; +} + +static void add_field(upb_msgdef *m, upb_fielddef *f, const void *ref_donor) { + release_containingtype(f); + f->msg.def = m; + f->msg_is_symbolic = false; + upb_inttable_insert(&m->itof, upb_fielddef_number(f), upb_value_ptr(f)); + upb_strtable_insert(&m->ntof, upb_fielddef_name(f), upb_value_ptr(f)); + upb_ref2(f, m); + upb_ref2(m, f); + if (ref_donor) upb_fielddef_unref(f, ref_donor); +} + +bool upb_msgdef_addfield(upb_msgdef *m, upb_fielddef *f, const void *ref_donor, + upb_status *s) { + /* TODO: extensions need to have a separate namespace, because proto2 allows a + * top-level extension (ie. one not in any package) to have the same name as a + * field from the message. + * + * This also implies that there needs to be a separate lookup-by-name method + * for extensions. It seems desirable for iteration to return both extensions + * and non-extensions though. + * + * We also need to validate that the field number is in an extension range iff + * it is an extension. + * + * This method is idempotent. Check if |f| is already part of this msgdef and + * return immediately if so. */ + if (upb_fielddef_containingtype(f) == m) { + return true; + } + + /* Check constraints for all fields before performing any action. */ + if (!check_field_add(m, f, s)) { + return false; + } else if (upb_fielddef_containingoneof(f) != NULL) { + /* Fields in a oneof can only be added by adding the oneof to the msgdef. */ + upb_status_seterrmsg(s, "fielddef is part of a oneof"); + return false; + } + + /* Constraint checks ok, perform the action. */ + add_field(m, f, ref_donor); + return true; +} + +bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor, + upb_status *s) { + upb_oneof_iter it; + + /* Check various conditions that would prevent this oneof from being added. */ + if (upb_oneofdef_containingtype(o)) { + upb_status_seterrmsg(s, "oneofdef already belongs to a message"); + return false; + } else if (upb_oneofdef_name(o) == NULL) { + upb_status_seterrmsg(s, "oneofdef name was not set"); + return false; + } else if (upb_msgdef_ntooz(m, upb_oneofdef_name(o))) { + upb_status_seterrmsg(s, "duplicate oneof name"); + return false; + } + + /* Check that all of the oneof's fields do not conflict with names or numbers + * of fields already in the message. */ + for (upb_oneof_begin(&it, o); !upb_oneof_done(&it); upb_oneof_next(&it)) { + const upb_fielddef *f = upb_oneof_iter_field(&it); + if (!check_field_add(m, f, s)) { + return false; + } + } + + /* Everything checks out -- commit now. */ + + /* Add oneof itself first. */ + o->parent = m; + upb_strtable_insert(&m->ntoo, upb_oneofdef_name(o), upb_value_ptr(o)); + upb_ref2(o, m); + upb_ref2(m, o); + + /* Add each field of the oneof directly to the msgdef. */ + for (upb_oneof_begin(&it, o); !upb_oneof_done(&it); upb_oneof_next(&it)) { + upb_fielddef *f = upb_oneof_iter_field(&it); + add_field(m, f, NULL); + } + + if (ref_donor) upb_oneofdef_unref(o, ref_donor); + + return true; +} + +const upb_fielddef *upb_msgdef_itof(const upb_msgdef *m, uint32_t i) { + upb_value val; + return upb_inttable_lookup32(&m->itof, i, &val) ? + upb_value_getptr(val) : NULL; +} + +const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name, + size_t len) { + upb_value val; + return upb_strtable_lookup2(&m->ntof, name, len, &val) ? + upb_value_getptr(val) : NULL; +} + +const upb_oneofdef *upb_msgdef_ntoo(const upb_msgdef *m, const char *name, + size_t len) { + upb_value val; + return upb_strtable_lookup2(&m->ntoo, name, len, &val) ? + upb_value_getptr(val) : NULL; +} + +int upb_msgdef_numfields(const upb_msgdef *m) { + return upb_strtable_count(&m->ntof); +} + +int upb_msgdef_numoneofs(const upb_msgdef *m) { + return upb_strtable_count(&m->ntoo); +} + +void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry) { + assert(!upb_msgdef_isfrozen(m)); + m->map_entry = map_entry; +} + +bool upb_msgdef_mapentry(const upb_msgdef *m) { + return m->map_entry; +} + +void upb_msg_field_begin(upb_msg_field_iter *iter, const upb_msgdef *m) { + upb_inttable_begin(iter, &m->itof); +} + +void upb_msg_field_next(upb_msg_field_iter *iter) { upb_inttable_next(iter); } + +bool upb_msg_field_done(const upb_msg_field_iter *iter) { + return upb_inttable_done(iter); +} + +upb_fielddef *upb_msg_iter_field(const upb_msg_field_iter *iter) { + return (upb_fielddef*)upb_value_getptr(upb_inttable_iter_value(iter)); +} + +void upb_msg_field_iter_setdone(upb_msg_field_iter *iter) { + upb_inttable_iter_setdone(iter); +} + +void upb_msg_oneof_begin(upb_msg_oneof_iter *iter, const upb_msgdef *m) { + upb_strtable_begin(iter, &m->ntoo); +} + +void upb_msg_oneof_next(upb_msg_oneof_iter *iter) { upb_strtable_next(iter); } + +bool upb_msg_oneof_done(const upb_msg_oneof_iter *iter) { + return upb_strtable_done(iter); +} + +upb_oneofdef *upb_msg_iter_oneof(const upb_msg_oneof_iter *iter) { + return (upb_oneofdef*)upb_value_getptr(upb_strtable_iter_value(iter)); +} + +void upb_msg_oneof_iter_setdone(upb_msg_oneof_iter *iter) { + upb_strtable_iter_setdone(iter); +} + +/* upb_oneofdef ***************************************************************/ + +static void visitoneof(const upb_refcounted *r, upb_refcounted_visit *visit, + void *closure) { + const upb_oneofdef *o = (const upb_oneofdef*)r; + upb_oneof_iter i; + for (upb_oneof_begin(&i, o); !upb_oneof_done(&i); upb_oneof_next(&i)) { + const upb_fielddef *f = upb_oneof_iter_field(&i); + visit(r, upb_fielddef_upcast2(f), closure); + } + if (o->parent) { + visit(r, upb_msgdef_upcast2(o->parent), closure); + } +} + +static void freeoneof(upb_refcounted *r) { + upb_oneofdef *o = (upb_oneofdef*)r; + upb_strtable_uninit(&o->ntof); + upb_inttable_uninit(&o->itof); + upb_def_uninit(upb_oneofdef_upcast_mutable(o)); + free(o); +} + +upb_oneofdef *upb_oneofdef_new(const void *owner) { + static const struct upb_refcounted_vtbl vtbl = {visitoneof, freeoneof}; + upb_oneofdef *o = malloc(sizeof(*o)); + o->parent = NULL; + if (!o) return NULL; + if (!upb_def_init(upb_oneofdef_upcast_mutable(o), UPB_DEF_ONEOF, &vtbl, + owner)) + goto err2; + if (!upb_inttable_init(&o->itof, UPB_CTYPE_PTR)) goto err2; + if (!upb_strtable_init(&o->ntof, UPB_CTYPE_PTR)) goto err1; + return o; + +err1: + upb_inttable_uninit(&o->itof); +err2: + free(o); + return NULL; +} + +upb_oneofdef *upb_oneofdef_dup(const upb_oneofdef *o, const void *owner) { + bool ok; + upb_oneof_iter i; + upb_oneofdef *newo = upb_oneofdef_new(owner); + if (!newo) return NULL; + ok = upb_def_setfullname(upb_oneofdef_upcast_mutable(newo), + upb_def_fullname(upb_oneofdef_upcast(o)), NULL); + UPB_ASSERT_VAR(ok, ok); + for (upb_oneof_begin(&i, o); !upb_oneof_done(&i); upb_oneof_next(&i)) { + upb_fielddef *f = upb_fielddef_dup(upb_oneof_iter_field(&i), &f); + if (!f || !upb_oneofdef_addfield(newo, f, &f, NULL)) { + upb_oneofdef_unref(newo, owner); + return NULL; + } + } + return newo; +} + +const char *upb_oneofdef_name(const upb_oneofdef *o) { + return upb_def_fullname(upb_oneofdef_upcast(o)); +} + +bool upb_oneofdef_setname(upb_oneofdef *o, const char *fullname, + upb_status *s) { + if (upb_oneofdef_containingtype(o)) { + upb_status_seterrmsg(s, "oneof already added to a message"); + return false; + } + return upb_def_setfullname(upb_oneofdef_upcast_mutable(o), fullname, s); +} + +const upb_msgdef *upb_oneofdef_containingtype(const upb_oneofdef *o) { + return o->parent; +} + +int upb_oneofdef_numfields(const upb_oneofdef *o) { + return upb_strtable_count(&o->ntof); +} + +bool upb_oneofdef_addfield(upb_oneofdef *o, upb_fielddef *f, + const void *ref_donor, + upb_status *s) { + assert(!upb_oneofdef_isfrozen(o)); + assert(!o->parent || !upb_msgdef_isfrozen(o->parent)); + + /* This method is idempotent. Check if |f| is already part of this oneofdef + * and return immediately if so. */ + if (upb_fielddef_containingoneof(f) == o) { + return true; + } + + /* The field must have an OPTIONAL label. */ + if (upb_fielddef_label(f) != UPB_LABEL_OPTIONAL) { + upb_status_seterrmsg(s, "fields in oneof must have OPTIONAL label"); + return false; + } + + /* Check that no field with this name or number exists already in the oneof. + * Also check that the field is not already part of a oneof. */ + if (upb_fielddef_name(f) == NULL || upb_fielddef_number(f) == 0) { + upb_status_seterrmsg(s, "field name or number were not set"); + return false; + } else if (upb_oneofdef_itof(o, upb_fielddef_number(f)) || + upb_oneofdef_ntofz(o, upb_fielddef_name(f))) { + upb_status_seterrmsg(s, "duplicate field name or number"); + return false; + } else if (upb_fielddef_containingoneof(f) != NULL) { + upb_status_seterrmsg(s, "fielddef already belongs to a oneof"); + return false; + } + + /* We allow adding a field to the oneof either if the field is not part of a + * msgdef, or if it is and we are also part of the same msgdef. */ + if (o->parent == NULL) { + /* If we're not in a msgdef, the field cannot be either. Otherwise we would + * need to magically add this oneof to a msgdef to remain consistent, which + * is surprising behavior. */ + if (upb_fielddef_containingtype(f) != NULL) { + upb_status_seterrmsg(s, "fielddef already belongs to a message, but " + "oneof does not"); + return false; + } + } else { + /* If we're in a msgdef, the user can add fields that either aren't in any + * msgdef (in which case they're added to our msgdef) or already a part of + * our msgdef. */ + if (upb_fielddef_containingtype(f) != NULL && + upb_fielddef_containingtype(f) != o->parent) { + upb_status_seterrmsg(s, "fielddef belongs to a different message " + "than oneof"); + return false; + } + } + + /* Commit phase. First add the field to our parent msgdef, if any, because + * that may fail; then add the field to our own tables. */ + + if (o->parent != NULL && upb_fielddef_containingtype(f) == NULL) { + if (!upb_msgdef_addfield((upb_msgdef*)o->parent, f, NULL, s)) { + return false; + } + } + + release_containingtype(f); + f->oneof = o; + upb_inttable_insert(&o->itof, upb_fielddef_number(f), upb_value_ptr(f)); + upb_strtable_insert(&o->ntof, upb_fielddef_name(f), upb_value_ptr(f)); + upb_ref2(f, o); + upb_ref2(o, f); + if (ref_donor) upb_fielddef_unref(f, ref_donor); + + return true; +} + +const upb_fielddef *upb_oneofdef_ntof(const upb_oneofdef *o, + const char *name, size_t length) { + upb_value val; + return upb_strtable_lookup2(&o->ntof, name, length, &val) ? + upb_value_getptr(val) : NULL; +} + +const upb_fielddef *upb_oneofdef_itof(const upb_oneofdef *o, uint32_t num) { + upb_value val; + return upb_inttable_lookup32(&o->itof, num, &val) ? + upb_value_getptr(val) : NULL; +} + +void upb_oneof_begin(upb_oneof_iter *iter, const upb_oneofdef *o) { + upb_inttable_begin(iter, &o->itof); +} + +void upb_oneof_next(upb_oneof_iter *iter) { + upb_inttable_next(iter); +} + +bool upb_oneof_done(upb_oneof_iter *iter) { + return upb_inttable_done(iter); +} + +upb_fielddef *upb_oneof_iter_field(const upb_oneof_iter *iter) { + return (upb_fielddef*)upb_value_getptr(upb_inttable_iter_value(iter)); +} + +void upb_oneof_iter_setdone(upb_oneof_iter *iter) { + upb_inttable_iter_setdone(iter); +} + + +#include +#include +#include + +typedef struct cleanup_ent { + upb_cleanup_func *cleanup; + void *ud; + struct cleanup_ent *next; +} cleanup_ent; + +static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, size_t size); + +/* Default allocator **********************************************************/ + +/* Just use realloc, keeping all allocated blocks in a linked list to destroy at + * the end. */ + +typedef struct mem_block { + /* List is doubly-linked, because in cases where realloc() moves an existing + * block, we need to be able to remove the old pointer from the list + * efficiently. */ + struct mem_block *prev, *next; +#ifndef NDEBUG + size_t size; /* Doesn't include mem_block structure. */ +#endif +} mem_block; + +typedef struct { + mem_block *head; +} default_alloc_ud; + +static void *default_alloc(void *_ud, void *ptr, size_t oldsize, size_t size) { + default_alloc_ud *ud = _ud; + mem_block *from, *block; + void *ret; + UPB_UNUSED(oldsize); + + from = ptr ? (void*)((char*)ptr - sizeof(mem_block)) : NULL; + +#ifndef NDEBUG + if (from) { + assert(oldsize <= from->size); + } +#endif + + /* TODO(haberman): we probably need to provide even better alignment here, + * like 16-byte alignment of the returned data pointer. */ + block = realloc(from, size + sizeof(mem_block)); + if (!block) return NULL; + ret = (char*)block + sizeof(*block); + +#ifndef NDEBUG + block->size = size; +#endif + + if (from) { + if (block != from) { + /* The block was moved, so pointers in next and prev blocks must be + * updated to its new location. */ + if (block->next) block->next->prev = block; + if (block->prev) block->prev->next = block; + if (ud->head == from) ud->head = block; + } + } else { + /* Insert at head of linked list. */ + block->prev = NULL; + block->next = ud->head; + if (block->next) block->next->prev = block; + ud->head = block; + } + + return ret; +} + +static void default_alloc_cleanup(void *_ud) { + default_alloc_ud *ud = _ud; + mem_block *block = ud->head; + + while (block) { + void *to_free = block; + block = block->next; + free(to_free); + } +} + + +/* Standard error functions ***************************************************/ + +static bool default_err(void *ud, const upb_status *status) { + UPB_UNUSED(ud); + UPB_UNUSED(status); + return false; +} + +static bool write_err_to(void *ud, const upb_status *status) { + upb_status *copy_to = ud; + upb_status_copy(copy_to, status); + return false; +} + + +/* upb_env ********************************************************************/ + +void upb_env_init(upb_env *e) { + default_alloc_ud *ud = (default_alloc_ud*)&e->default_alloc_ud; + e->ok_ = true; + e->bytes_allocated = 0; + e->cleanup_head = NULL; + + ud->head = NULL; + + /* Set default functions. */ + upb_env_setallocfunc(e, default_alloc, ud); + upb_env_seterrorfunc(e, default_err, NULL); +} + +void upb_env_uninit(upb_env *e) { + cleanup_ent *ent = e->cleanup_head; + + while (ent) { + ent->cleanup(ent->ud); + ent = ent->next; + } + + /* Must do this after running cleanup functions, because this will delete + the memory we store our cleanup entries in! */ + if (e->alloc == default_alloc) { + default_alloc_cleanup(e->alloc_ud); + } +} + +UPB_FORCEINLINE void upb_env_setallocfunc(upb_env *e, upb_alloc_func *alloc, + void *ud) { + e->alloc = alloc; + e->alloc_ud = ud; +} + +UPB_FORCEINLINE void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, + void *ud) { + e->err = func; + e->err_ud = ud; +} + +void upb_env_reporterrorsto(upb_env *e, upb_status *status) { + e->err = write_err_to; + e->err_ud = status; +} + +bool upb_env_ok(const upb_env *e) { + return e->ok_; +} + +bool upb_env_reporterror(upb_env *e, const upb_status *status) { + e->ok_ = false; + return e->err(e->err_ud, status); +} + +bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) { + cleanup_ent *ent = upb_env_malloc(e, sizeof(cleanup_ent)); + if (!ent) return false; + + ent->cleanup = func; + ent->ud = ud; + ent->next = e->cleanup_head; + e->cleanup_head = ent; + + return true; +} + +void *upb_env_malloc(upb_env *e, size_t size) { + e->bytes_allocated += size; + if (e->alloc == seeded_alloc) { + /* This is equivalent to the next branch, but allows inlining for a + * measurable perf benefit. */ + return seeded_alloc(e->alloc_ud, NULL, 0, size); + } else { + return e->alloc(e->alloc_ud, NULL, 0, size); + } +} + +void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) { + char *ret; + assert(oldsize <= size); + ret = e->alloc(e->alloc_ud, ptr, oldsize, size); + +#ifndef NDEBUG + /* Overwrite non-preserved memory to ensure callers are passing the oldsize + * that they truly require. */ + memset(ret + oldsize, 0xff, size - oldsize); +#endif + + return ret; +} + +size_t upb_env_bytesallocated(const upb_env *e) { + return e->bytes_allocated; +} + + +/* upb_seededalloc ************************************************************/ + +/* Be conservative and choose 16 in case anyone is using SSE. */ +static const size_t maxalign = 16; + +static size_t align_up(size_t size) { + return ((size + maxalign - 1) / maxalign) * maxalign; +} + +UPB_FORCEINLINE static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, + size_t size) { + upb_seededalloc *a = ud; + + size = align_up(size); + + assert(a->mem_limit >= a->mem_ptr); + + if (oldsize == 0 && size <= (size_t)(a->mem_limit - a->mem_ptr)) { + /* Fast path: we can satisfy from the initial allocation. */ + void *ret = a->mem_ptr; + a->mem_ptr += size; + return ret; + } else { + char *chptr = ptr; + /* Slow path: fallback to other allocator. */ + a->need_cleanup = true; + /* Is `ptr` part of the user-provided initial block? Don't pass it to the + * default allocator if so; otherwise, it may try to realloc() the block. */ + if (chptr >= a->mem_base && chptr < a->mem_limit) { + void *ret; + assert(chptr + oldsize <= a->mem_limit); + ret = a->alloc(a->alloc_ud, NULL, 0, size); + if (ret) memcpy(ret, ptr, oldsize); + return ret; + } else { + return a->alloc(a->alloc_ud, ptr, oldsize, size); + } + } +} + +void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len) { + default_alloc_ud *ud = (default_alloc_ud*)&a->default_alloc_ud; + a->mem_base = mem; + a->mem_ptr = mem; + a->mem_limit = (char*)mem + len; + a->need_cleanup = false; + a->returned_allocfunc = false; + + ud->head = NULL; + + upb_seededalloc_setfallbackalloc(a, default_alloc, ud); +} + +void upb_seededalloc_uninit(upb_seededalloc *a) { + if (a->alloc == default_alloc && a->need_cleanup) { + default_alloc_cleanup(a->alloc_ud); + } +} + +UPB_FORCEINLINE void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, + upb_alloc_func *alloc, + void *ud) { + assert(!a->returned_allocfunc); + a->alloc = alloc; + a->alloc_ud = ud; +} + +upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a) { + a->returned_allocfunc = true; + return seeded_alloc; +} +/* +** TODO(haberman): it's unclear whether a lot of the consistency checks should +** assert() or return false. +*/ + + +#include +#include + + + +/* Defined for the sole purpose of having a unique pointer value for + * UPB_NO_CLOSURE. */ +char _upb_noclosure; + +static void freehandlers(upb_refcounted *r) { + upb_handlers *h = (upb_handlers*)r; + + upb_inttable_iter i; + upb_inttable_begin(&i, &h->cleanup_); + for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { + void *val = (void*)upb_inttable_iter_key(&i); + upb_value func_val = upb_inttable_iter_value(&i); + upb_handlerfree *func = upb_value_getfptr(func_val); + func(val); + } + + upb_inttable_uninit(&h->cleanup_); + upb_msgdef_unref(h->msg, h); + free(h->sub); + free(h); +} + +static void visithandlers(const upb_refcounted *r, upb_refcounted_visit *visit, + void *closure) { + const upb_handlers *h = (const upb_handlers*)r; + upb_msg_field_iter i; + for(upb_msg_field_begin(&i, h->msg); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { + upb_fielddef *f = upb_msg_iter_field(&i); + const upb_handlers *sub; + if (!upb_fielddef_issubmsg(f)) continue; + sub = upb_handlers_getsubhandlers(h, f); + if (sub) visit(r, upb_handlers_upcast(sub), closure); + } +} + +static const struct upb_refcounted_vtbl vtbl = {visithandlers, freehandlers}; + +typedef struct { + upb_inttable tab; /* maps upb_msgdef* -> upb_handlers*. */ + upb_handlers_callback *callback; + const void *closure; +} dfs_state; + +/* TODO(haberman): discard upb_handlers* objects that do not actually have any + * handlers set and cannot reach any upb_handlers* object that does. This is + * slightly tricky to do correctly. */ +static upb_handlers *newformsg(const upb_msgdef *m, const void *owner, + dfs_state *s) { + upb_msg_field_iter i; + upb_handlers *h = upb_handlers_new(m, owner); + if (!h) return NULL; + if (!upb_inttable_insertptr(&s->tab, m, upb_value_ptr(h))) goto oom; + + s->callback(s->closure, h); + + /* For each submessage field, get or create a handlers object and set it as + * the subhandlers. */ + for(upb_msg_field_begin(&i, m); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { + upb_fielddef *f = upb_msg_iter_field(&i); + const upb_msgdef *subdef; + upb_value subm_ent; + + if (!upb_fielddef_issubmsg(f)) continue; + + subdef = upb_downcast_msgdef(upb_fielddef_subdef(f)); + if (upb_inttable_lookupptr(&s->tab, subdef, &subm_ent)) { + upb_handlers_setsubhandlers(h, f, upb_value_getptr(subm_ent)); + } else { + upb_handlers *sub_mh = newformsg(subdef, &sub_mh, s); + if (!sub_mh) goto oom; + upb_handlers_setsubhandlers(h, f, sub_mh); + upb_handlers_unref(sub_mh, &sub_mh); + } + } + return h; + +oom: + upb_handlers_unref(h, owner); + return NULL; +} + +/* Given a selector for a STARTSUBMSG handler, resolves to a pointer to the + * subhandlers for this submessage field. */ +#define SUBH(h, selector) (h->sub[selector]) + +/* The selector for a submessage field is the field index. */ +#define SUBH_F(h, f) SUBH(h, f->index_) + +static int32_t trygetsel(upb_handlers *h, const upb_fielddef *f, + upb_handlertype_t type) { + upb_selector_t sel; + assert(!upb_handlers_isfrozen(h)); + if (upb_handlers_msgdef(h) != upb_fielddef_containingtype(f)) { + upb_status_seterrf( + &h->status_, "type mismatch: field %s does not belong to message %s", + upb_fielddef_name(f), upb_msgdef_fullname(upb_handlers_msgdef(h))); + return -1; + } + if (!upb_handlers_getselector(f, type, &sel)) { + upb_status_seterrf( + &h->status_, + "type mismatch: cannot register handler type %d for field %s", + type, upb_fielddef_name(f)); + return -1; + } + return sel; +} + +static upb_selector_t handlers_getsel(upb_handlers *h, const upb_fielddef *f, + upb_handlertype_t type) { + int32_t sel = trygetsel(h, f, type); + assert(sel >= 0); + return sel; +} + +static const void **returntype(upb_handlers *h, const upb_fielddef *f, + upb_handlertype_t type) { + return &h->table[handlers_getsel(h, f, type)].attr.return_closure_type_; +} + +static bool doset(upb_handlers *h, int32_t sel, const upb_fielddef *f, + upb_handlertype_t type, upb_func *func, + upb_handlerattr *attr) { + upb_handlerattr set_attr = UPB_HANDLERATTR_INITIALIZER; + const void *closure_type; + const void **context_closure_type; + + assert(!upb_handlers_isfrozen(h)); + + if (sel < 0) { + upb_status_seterrmsg(&h->status_, + "incorrect handler type for this field."); + return false; + } + + if (h->table[sel].func) { + upb_status_seterrmsg(&h->status_, + "cannot change handler once it has been set."); + return false; + } + + if (attr) { + set_attr = *attr; + } + + /* Check that the given closure type matches the closure type that has been + * established for this context (if any). */ + closure_type = upb_handlerattr_closuretype(&set_attr); + + if (type == UPB_HANDLER_STRING) { + context_closure_type = returntype(h, f, UPB_HANDLER_STARTSTR); + } else if (f && upb_fielddef_isseq(f) && + type != UPB_HANDLER_STARTSEQ && + type != UPB_HANDLER_ENDSEQ) { + context_closure_type = returntype(h, f, UPB_HANDLER_STARTSEQ); + } else { + context_closure_type = &h->top_closure_type; + } + + if (closure_type && *context_closure_type && + closure_type != *context_closure_type) { + /* TODO(haberman): better message for debugging. */ + if (f) { + upb_status_seterrf(&h->status_, + "closure type does not match for field %s", + upb_fielddef_name(f)); + } else { + upb_status_seterrmsg( + &h->status_, "closure type does not match for message-level handler"); + } + return false; + } + + if (closure_type) + *context_closure_type = closure_type; + + /* If this is a STARTSEQ or STARTSTR handler, check that the returned pointer + * matches any pre-existing expectations about what type is expected. */ + if (type == UPB_HANDLER_STARTSEQ || type == UPB_HANDLER_STARTSTR) { + const void *return_type = upb_handlerattr_returnclosuretype(&set_attr); + const void *table_return_type = + upb_handlerattr_returnclosuretype(&h->table[sel].attr); + if (return_type && table_return_type && return_type != table_return_type) { + upb_status_seterrmsg(&h->status_, "closure return type does not match"); + return false; + } + + if (table_return_type && !return_type) + upb_handlerattr_setreturnclosuretype(&set_attr, table_return_type); + } + + h->table[sel].func = (upb_func*)func; + h->table[sel].attr = set_attr; + return true; +} + +/* Returns the effective closure type for this handler (which will propagate + * from outer frames if this frame has no START* handler). Not implemented for + * UPB_HANDLER_STRING at the moment since this is not needed. Returns NULL is + * the effective closure type is unspecified (either no handler was registered + * to specify it or the handler that was registered did not specify the closure + * type). */ +const void *effective_closure_type(upb_handlers *h, const upb_fielddef *f, + upb_handlertype_t type) { + const void *ret; + upb_selector_t sel; + + assert(type != UPB_HANDLER_STRING); + ret = h->top_closure_type; + + if (upb_fielddef_isseq(f) && + type != UPB_HANDLER_STARTSEQ && + type != UPB_HANDLER_ENDSEQ && + h->table[sel = handlers_getsel(h, f, UPB_HANDLER_STARTSEQ)].func) { + ret = upb_handlerattr_returnclosuretype(&h->table[sel].attr); + } + + if (type == UPB_HANDLER_STRING && + h->table[sel = handlers_getsel(h, f, UPB_HANDLER_STARTSTR)].func) { + ret = upb_handlerattr_returnclosuretype(&h->table[sel].attr); + } + + /* The effective type of the submessage; not used yet. + * if (type == SUBMESSAGE && + * h->table[sel = handlers_getsel(h, f, UPB_HANDLER_STARTSUBMSG)].func) { + * ret = upb_handlerattr_returnclosuretype(&h->table[sel].attr); + * } */ + + return ret; +} + +/* Checks whether the START* handler specified by f & type is missing even + * though it is required to convert the established type of an outer frame + * ("closure_type") into the established type of an inner frame (represented in + * the return closure type of this handler's attr. */ +bool checkstart(upb_handlers *h, const upb_fielddef *f, upb_handlertype_t type, + upb_status *status) { + const void *closure_type; + const upb_handlerattr *attr; + const void *return_closure_type; + + upb_selector_t sel = handlers_getsel(h, f, type); + if (h->table[sel].func) return true; + closure_type = effective_closure_type(h, f, type); + attr = &h->table[sel].attr; + return_closure_type = upb_handlerattr_returnclosuretype(attr); + if (closure_type && return_closure_type && + closure_type != return_closure_type) { + upb_status_seterrf(status, + "expected start handler to return sub type for field %f", + upb_fielddef_name(f)); + return false; + } + return true; +} + +/* Public interface ***********************************************************/ + +upb_handlers *upb_handlers_new(const upb_msgdef *md, const void *owner) { + int extra; + upb_handlers *h; + + assert(upb_msgdef_isfrozen(md)); + + extra = sizeof(upb_handlers_tabent) * (md->selector_count - 1); + h = calloc(sizeof(*h) + extra, 1); + if (!h) return NULL; + + h->msg = md; + upb_msgdef_ref(h->msg, h); + upb_status_clear(&h->status_); + h->sub = calloc(md->submsg_field_count, sizeof(*h->sub)); + if (!h->sub) goto oom; + if (!upb_refcounted_init(upb_handlers_upcast_mutable(h), &vtbl, owner)) + goto oom; + if (!upb_inttable_init(&h->cleanup_, UPB_CTYPE_FPTR)) goto oom; + + /* calloc() above initialized all handlers to NULL. */ + return h; + +oom: + freehandlers(upb_handlers_upcast_mutable(h)); + return NULL; +} + +const upb_handlers *upb_handlers_newfrozen(const upb_msgdef *m, + const void *owner, + upb_handlers_callback *callback, + const void *closure) { + dfs_state state; + upb_handlers *ret; + bool ok; + upb_refcounted *r; + + state.callback = callback; + state.closure = closure; + if (!upb_inttable_init(&state.tab, UPB_CTYPE_PTR)) return NULL; + + ret = newformsg(m, owner, &state); + + upb_inttable_uninit(&state.tab); + if (!ret) return NULL; + + r = upb_handlers_upcast_mutable(ret); + ok = upb_refcounted_freeze(&r, 1, NULL, UPB_MAX_HANDLER_DEPTH); + UPB_ASSERT_VAR(ok, ok); + + return ret; +} + +const upb_status *upb_handlers_status(upb_handlers *h) { + assert(!upb_handlers_isfrozen(h)); + return &h->status_; +} + +void upb_handlers_clearerr(upb_handlers *h) { + assert(!upb_handlers_isfrozen(h)); + upb_status_clear(&h->status_); +} + +#define SETTER(name, handlerctype, handlertype) \ + bool upb_handlers_set ## name(upb_handlers *h, const upb_fielddef *f, \ + handlerctype func, upb_handlerattr *attr) { \ + int32_t sel = trygetsel(h, f, handlertype); \ + return doset(h, sel, f, handlertype, (upb_func*)func, attr); \ + } + +SETTER(int32, upb_int32_handlerfunc*, UPB_HANDLER_INT32) +SETTER(int64, upb_int64_handlerfunc*, UPB_HANDLER_INT64) +SETTER(uint32, upb_uint32_handlerfunc*, UPB_HANDLER_UINT32) +SETTER(uint64, upb_uint64_handlerfunc*, UPB_HANDLER_UINT64) +SETTER(float, upb_float_handlerfunc*, UPB_HANDLER_FLOAT) +SETTER(double, upb_double_handlerfunc*, UPB_HANDLER_DOUBLE) +SETTER(bool, upb_bool_handlerfunc*, UPB_HANDLER_BOOL) +SETTER(startstr, upb_startstr_handlerfunc*, UPB_HANDLER_STARTSTR) +SETTER(string, upb_string_handlerfunc*, UPB_HANDLER_STRING) +SETTER(endstr, upb_endfield_handlerfunc*, UPB_HANDLER_ENDSTR) +SETTER(startseq, upb_startfield_handlerfunc*, UPB_HANDLER_STARTSEQ) +SETTER(startsubmsg, upb_startfield_handlerfunc*, UPB_HANDLER_STARTSUBMSG) +SETTER(endsubmsg, upb_endfield_handlerfunc*, UPB_HANDLER_ENDSUBMSG) +SETTER(endseq, upb_endfield_handlerfunc*, UPB_HANDLER_ENDSEQ) + +#undef SETTER + +bool upb_handlers_setstartmsg(upb_handlers *h, upb_startmsg_handlerfunc *func, + upb_handlerattr *attr) { + return doset(h, UPB_STARTMSG_SELECTOR, NULL, UPB_HANDLER_INT32, + (upb_func *)func, attr); +} + +bool upb_handlers_setendmsg(upb_handlers *h, upb_endmsg_handlerfunc *func, + upb_handlerattr *attr) { + assert(!upb_handlers_isfrozen(h)); + return doset(h, UPB_ENDMSG_SELECTOR, NULL, UPB_HANDLER_INT32, + (upb_func *)func, attr); +} + +bool upb_handlers_setsubhandlers(upb_handlers *h, const upb_fielddef *f, + const upb_handlers *sub) { + assert(sub); + assert(!upb_handlers_isfrozen(h)); + assert(upb_fielddef_issubmsg(f)); + if (SUBH_F(h, f)) return false; /* Can't reset. */ + if (upb_msgdef_upcast(upb_handlers_msgdef(sub)) != upb_fielddef_subdef(f)) { + return false; + } + SUBH_F(h, f) = sub; + upb_ref2(sub, h); + return true; +} + +const upb_handlers *upb_handlers_getsubhandlers(const upb_handlers *h, + const upb_fielddef *f) { + assert(upb_fielddef_issubmsg(f)); + return SUBH_F(h, f); +} + +bool upb_handlers_getattr(const upb_handlers *h, upb_selector_t sel, + upb_handlerattr *attr) { + if (!upb_handlers_gethandler(h, sel)) + return false; + *attr = h->table[sel].attr; + return true; +} + +const upb_handlers *upb_handlers_getsubhandlers_sel(const upb_handlers *h, + upb_selector_t sel) { + /* STARTSUBMSG selector in sel is the field's selector base. */ + return SUBH(h, sel - UPB_STATIC_SELECTOR_COUNT); +} + +const upb_msgdef *upb_handlers_msgdef(const upb_handlers *h) { return h->msg; } + +bool upb_handlers_addcleanup(upb_handlers *h, void *p, upb_handlerfree *func) { + bool ok; + if (upb_inttable_lookupptr(&h->cleanup_, p, NULL)) { + return false; + } + ok = upb_inttable_insertptr(&h->cleanup_, p, upb_value_fptr(func)); + UPB_ASSERT_VAR(ok, ok); + return true; +} + + +/* "Static" methods ***********************************************************/ + +bool upb_handlers_freeze(upb_handlers *const*handlers, int n, upb_status *s) { + /* TODO: verify we have a transitive closure. */ + int i; + for (i = 0; i < n; i++) { + upb_msg_field_iter j; + upb_handlers *h = handlers[i]; + + if (!upb_ok(&h->status_)) { + upb_status_seterrf(s, "handlers for message %s had error status: %s", + upb_msgdef_fullname(upb_handlers_msgdef(h)), + upb_status_errmsg(&h->status_)); + return false; + } + + /* Check that there are no closure mismatches due to missing Start* handlers + * or subhandlers with different type-level types. */ + for(upb_msg_field_begin(&j, h->msg); + !upb_msg_field_done(&j); + upb_msg_field_next(&j)) { + + const upb_fielddef *f = upb_msg_iter_field(&j); + if (upb_fielddef_isseq(f)) { + if (!checkstart(h, f, UPB_HANDLER_STARTSEQ, s)) + return false; + } + + if (upb_fielddef_isstring(f)) { + if (!checkstart(h, f, UPB_HANDLER_STARTSTR, s)) + return false; + } + + if (upb_fielddef_issubmsg(f)) { + bool hashandler = false; + if (upb_handlers_gethandler( + h, handlers_getsel(h, f, UPB_HANDLER_STARTSUBMSG)) || + upb_handlers_gethandler( + h, handlers_getsel(h, f, UPB_HANDLER_ENDSUBMSG))) { + hashandler = true; + } + + if (upb_fielddef_isseq(f) && + (upb_handlers_gethandler( + h, handlers_getsel(h, f, UPB_HANDLER_STARTSEQ)) || + upb_handlers_gethandler( + h, handlers_getsel(h, f, UPB_HANDLER_ENDSEQ)))) { + hashandler = true; + } + + if (hashandler && !upb_handlers_getsubhandlers(h, f)) { + /* For now we add an empty subhandlers in this case. It makes the + * decoder code generator simpler, because it only has to handle two + * cases (submessage has handlers or not) as opposed to three + * (submessage has handlers in enclosing message but no subhandlers). + * + * This makes parsing less efficient in the case that we want to + * notice a submessage but skip its contents (like if we're testing + * for submessage presence or counting the number of repeated + * submessages). In this case we will end up parsing the submessage + * field by field and throwing away the results for each, instead of + * skipping the whole delimited thing at once. If this is an issue we + * can revisit it, but do remember that this only arises when you have + * handlers (startseq/startsubmsg/endsubmsg/endseq) set for the + * submessage but no subhandlers. The uses cases for this are + * limited. */ + upb_handlers *sub = upb_handlers_new(upb_fielddef_msgsubdef(f), &sub); + upb_handlers_setsubhandlers(h, f, sub); + upb_handlers_unref(sub, &sub); + } + + /* TODO(haberman): check type of submessage. + * This is slightly tricky; also consider whether we should check that + * they match at setsubhandlers time. */ + } + } + } + + if (!upb_refcounted_freeze((upb_refcounted*const*)handlers, n, s, + UPB_MAX_HANDLER_DEPTH)) { + return false; + } + + return true; +} + +upb_handlertype_t upb_handlers_getprimitivehandlertype(const upb_fielddef *f) { + switch (upb_fielddef_type(f)) { + case UPB_TYPE_INT32: + case UPB_TYPE_ENUM: return UPB_HANDLER_INT32; + case UPB_TYPE_INT64: return UPB_HANDLER_INT64; + case UPB_TYPE_UINT32: return UPB_HANDLER_UINT32; + case UPB_TYPE_UINT64: return UPB_HANDLER_UINT64; + case UPB_TYPE_FLOAT: return UPB_HANDLER_FLOAT; + case UPB_TYPE_DOUBLE: return UPB_HANDLER_DOUBLE; + case UPB_TYPE_BOOL: return UPB_HANDLER_BOOL; + default: assert(false); return -1; /* Invalid input. */ + } +} + +bool upb_handlers_getselector(const upb_fielddef *f, upb_handlertype_t type, + upb_selector_t *s) { + switch (type) { + case UPB_HANDLER_INT32: + case UPB_HANDLER_INT64: + case UPB_HANDLER_UINT32: + case UPB_HANDLER_UINT64: + case UPB_HANDLER_FLOAT: + case UPB_HANDLER_DOUBLE: + case UPB_HANDLER_BOOL: + if (!upb_fielddef_isprimitive(f) || + upb_handlers_getprimitivehandlertype(f) != type) + return false; + *s = f->selector_base; + break; + case UPB_HANDLER_STRING: + if (upb_fielddef_isstring(f)) { + *s = f->selector_base; + } else if (upb_fielddef_lazy(f)) { + *s = f->selector_base + 3; + } else { + return false; + } + break; + case UPB_HANDLER_STARTSTR: + if (upb_fielddef_isstring(f) || upb_fielddef_lazy(f)) { + *s = f->selector_base + 1; + } else { + return false; + } + break; + case UPB_HANDLER_ENDSTR: + if (upb_fielddef_isstring(f) || upb_fielddef_lazy(f)) { + *s = f->selector_base + 2; + } else { + return false; + } + break; + case UPB_HANDLER_STARTSEQ: + if (!upb_fielddef_isseq(f)) return false; + *s = f->selector_base - 2; + break; + case UPB_HANDLER_ENDSEQ: + if (!upb_fielddef_isseq(f)) return false; + *s = f->selector_base - 1; + break; + case UPB_HANDLER_STARTSUBMSG: + if (!upb_fielddef_issubmsg(f)) return false; + /* Selectors for STARTSUBMSG are at the beginning of the table so that the + * selector can also be used as an index into the "sub" array of + * subhandlers. The indexes for the two into these two tables are the + * same, except that in the handler table the static selectors come first. */ + *s = f->index_ + UPB_STATIC_SELECTOR_COUNT; + break; + case UPB_HANDLER_ENDSUBMSG: + if (!upb_fielddef_issubmsg(f)) return false; + *s = f->selector_base; + break; + } + assert((size_t)*s < upb_fielddef_containingtype(f)->selector_count); + return true; +} + +uint32_t upb_handlers_selectorbaseoffset(const upb_fielddef *f) { + return upb_fielddef_isseq(f) ? 2 : 0; +} + +uint32_t upb_handlers_selectorcount(const upb_fielddef *f) { + uint32_t ret = 1; + if (upb_fielddef_isseq(f)) ret += 2; /* STARTSEQ/ENDSEQ */ + if (upb_fielddef_isstring(f)) ret += 2; /* [STRING]/STARTSTR/ENDSTR */ + if (upb_fielddef_issubmsg(f)) { + /* ENDSUBMSG (STARTSUBMSG is at table beginning) */ + ret += 0; + if (upb_fielddef_lazy(f)) { + /* STARTSTR/ENDSTR/STRING (for lazy) */ + ret += 3; + } + } + return ret; +} + + +/* upb_handlerattr ************************************************************/ + +void upb_handlerattr_init(upb_handlerattr *attr) { + upb_handlerattr from = UPB_HANDLERATTR_INITIALIZER; + memcpy(attr, &from, sizeof(*attr)); +} + +void upb_handlerattr_uninit(upb_handlerattr *attr) { + UPB_UNUSED(attr); +} + +bool upb_handlerattr_sethandlerdata(upb_handlerattr *attr, const void *hd) { + attr->handler_data_ = hd; + return true; +} + +bool upb_handlerattr_setclosuretype(upb_handlerattr *attr, const void *type) { + attr->closure_type_ = type; + return true; +} + +const void *upb_handlerattr_closuretype(const upb_handlerattr *attr) { + return attr->closure_type_; +} + +bool upb_handlerattr_setreturnclosuretype(upb_handlerattr *attr, + const void *type) { + attr->return_closure_type_ = type; + return true; +} + +const void *upb_handlerattr_returnclosuretype(const upb_handlerattr *attr) { + return attr->return_closure_type_; +} + +bool upb_handlerattr_setalwaysok(upb_handlerattr *attr, bool alwaysok) { + attr->alwaysok_ = alwaysok; + return true; +} + +bool upb_handlerattr_alwaysok(const upb_handlerattr *attr) { + return attr->alwaysok_; +} + +/* upb_bufhandle **************************************************************/ + +size_t upb_bufhandle_objofs(const upb_bufhandle *h) { + return h->objofs_; +} + +/* upb_byteshandler ***********************************************************/ + +void upb_byteshandler_init(upb_byteshandler* h) { + memset(h, 0, sizeof(*h)); +} + +/* For when we support handlerfree callbacks. */ +void upb_byteshandler_uninit(upb_byteshandler* h) { + UPB_UNUSED(h); +} + +bool upb_byteshandler_setstartstr(upb_byteshandler *h, + upb_startstr_handlerfunc *func, void *d) { + h->table[UPB_STARTSTR_SELECTOR].func = (upb_func*)func; + h->table[UPB_STARTSTR_SELECTOR].attr.handler_data_ = d; + return true; +} + +bool upb_byteshandler_setstring(upb_byteshandler *h, + upb_string_handlerfunc *func, void *d) { + h->table[UPB_STRING_SELECTOR].func = (upb_func*)func; + h->table[UPB_STRING_SELECTOR].attr.handler_data_ = d; + return true; +} + +bool upb_byteshandler_setendstr(upb_byteshandler *h, + upb_endfield_handlerfunc *func, void *d) { + h->table[UPB_ENDSTR_SELECTOR].func = (upb_func*)func; + h->table[UPB_ENDSTR_SELECTOR].attr.handler_data_ = d; + return true; +} +/* +** upb::RefCounted Implementation +** +** Our key invariants are: +** 1. reference cycles never span groups +** 2. for ref2(to, from), we increment to's count iff group(from) != group(to) +** +** The previous two are how we avoid leaking cycles. Other important +** invariants are: +** 3. for mutable objects "from" and "to", if there exists a ref2(to, from) +** this implies group(from) == group(to). (In practice, what we implement +** is even stronger; "from" and "to" will share a group if there has *ever* +** been a ref2(to, from), but all that is necessary for correctness is the +** weaker one). +** 4. mutable and immutable objects are never in the same group. +*/ + + +#include +#include + +static void freeobj(upb_refcounted *o); + +const char untracked_val; +const void *UPB_UNTRACKED_REF = &untracked_val; + +/* arch-specific atomic primitives *******************************************/ + +#ifdef UPB_THREAD_UNSAFE /*---------------------------------------------------*/ + +static void atomic_inc(uint32_t *a) { (*a)++; } +static bool atomic_dec(uint32_t *a) { return --(*a) == 0; } + +#elif defined(__GNUC__) || defined(__clang__) /*------------------------------*/ + +static void atomic_inc(uint32_t *a) { __sync_fetch_and_add(a, 1); } +static bool atomic_dec(uint32_t *a) { return __sync_sub_and_fetch(a, 1) == 0; } + +#elif defined(WIN32) /*-------------------------------------------------------*/ + +#include + +static void atomic_inc(upb_atomic_t *a) { InterlockedIncrement(&a->val); } +static bool atomic_dec(upb_atomic_t *a) { + return InterlockedDecrement(&a->val) == 0; +} + +#else +#error Atomic primitives not defined for your platform/CPU. \ + Implement them or compile with UPB_THREAD_UNSAFE. +#endif + +/* All static objects point to this refcount. + * It is special-cased in ref/unref below. */ +uint32_t static_refcount = -1; + +/* We can avoid atomic ops for statically-declared objects. + * This is a minor optimization but nice since we can avoid degrading under + * contention in this case. */ + +static void refgroup(uint32_t *group) { + if (group != &static_refcount) + atomic_inc(group); +} + +static bool unrefgroup(uint32_t *group) { + if (group == &static_refcount) { + return false; + } else { + return atomic_dec(group); + } +} + + +/* Reference tracking (debug only) ********************************************/ + +#ifdef UPB_DEBUG_REFS + +#ifdef UPB_THREAD_UNSAFE + +static void upb_lock() {} +static void upb_unlock() {} + +#else + +/* User must define functions that lock/unlock a global mutex and link this + * file against them. */ +void upb_lock(); +void upb_unlock(); + +#endif + +/* UPB_DEBUG_REFS mode counts on being able to malloc() memory in some + * code-paths that can normally never fail, like upb_refcounted_ref(). Since + * we have no way to propagage out-of-memory errors back to the user, and since + * these errors can only occur in UPB_DEBUG_REFS mode, we immediately fail. */ +#define CHECK_OOM(predicate) if (!(predicate)) { assert(predicate); exit(1); } + +typedef struct { + int count; /* How many refs there are (duplicates only allowed for ref2). */ + bool is_ref2; +} trackedref; + +static trackedref *trackedref_new(bool is_ref2) { + trackedref *ret = malloc(sizeof(*ret)); + CHECK_OOM(ret); + ret->count = 1; + ret->is_ref2 = is_ref2; + return ret; +} + +static void track(const upb_refcounted *r, const void *owner, bool ref2) { + upb_value v; + + assert(owner); + if (owner == UPB_UNTRACKED_REF) return; + + upb_lock(); + if (upb_inttable_lookupptr(r->refs, owner, &v)) { + trackedref *ref = upb_value_getptr(v); + /* Since we allow multiple ref2's for the same to/from pair without + * allocating separate memory for each one, we lose the fine-grained + * tracking behavior we get with regular refs. Since ref2s only happen + * inside upb, we'll accept this limitation until/unless there is a really + * difficult upb-internal bug that can't be figured out without it. */ + assert(ref2); + assert(ref->is_ref2); + ref->count++; + } else { + trackedref *ref = trackedref_new(ref2); + bool ok = upb_inttable_insertptr(r->refs, owner, upb_value_ptr(ref)); + CHECK_OOM(ok); + if (ref2) { + /* We know this cast is safe when it is a ref2, because it's coming from + * another refcounted object. */ + const upb_refcounted *from = owner; + assert(!upb_inttable_lookupptr(from->ref2s, r, NULL)); + ok = upb_inttable_insertptr(from->ref2s, r, upb_value_ptr(NULL)); + CHECK_OOM(ok); + } + } + upb_unlock(); +} + +static void untrack(const upb_refcounted *r, const void *owner, bool ref2) { + upb_value v; + bool found; + trackedref *ref; + + assert(owner); + if (owner == UPB_UNTRACKED_REF) return; + + upb_lock(); + found = upb_inttable_lookupptr(r->refs, owner, &v); + /* This assert will fail if an owner attempts to release a ref it didn't have. */ + UPB_ASSERT_VAR(found, found); + ref = upb_value_getptr(v); + assert(ref->is_ref2 == ref2); + if (--ref->count == 0) { + free(ref); + upb_inttable_removeptr(r->refs, owner, NULL); + if (ref2) { + /* We know this cast is safe when it is a ref2, because it's coming from + * another refcounted object. */ + const upb_refcounted *from = owner; + bool removed = upb_inttable_removeptr(from->ref2s, r, NULL); + assert(removed); + } + } + upb_unlock(); +} + +static void checkref(const upb_refcounted *r, const void *owner, bool ref2) { + upb_value v; + bool found; + trackedref *ref; + + upb_lock(); + found = upb_inttable_lookupptr(r->refs, owner, &v); + UPB_ASSERT_VAR(found, found); + ref = upb_value_getptr(v); + assert(ref->is_ref2 == ref2); + upb_unlock(); +} + +/* Populates the given UPB_CTYPE_INT32 inttable with counts of ref2's that + * originate from the given owner. */ +static void getref2s(const upb_refcounted *owner, upb_inttable *tab) { + upb_inttable_iter i; + + upb_lock(); + upb_inttable_begin(&i, owner->ref2s); + for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { + upb_value v; + upb_value count; + trackedref *ref; + bool ok; + bool found; + + upb_refcounted *to = (upb_refcounted*)upb_inttable_iter_key(&i); + + /* To get the count we need to look in the target's table. */ + found = upb_inttable_lookupptr(to->refs, owner, &v); + assert(found); + ref = upb_value_getptr(v); + count = upb_value_int32(ref->count); + + ok = upb_inttable_insertptr(tab, to, count); + CHECK_OOM(ok); + } + upb_unlock(); +} + +typedef struct { + upb_inttable ref2; + const upb_refcounted *obj; +} check_state; + +static void visit_check(const upb_refcounted *obj, const upb_refcounted *subobj, + void *closure) { + check_state *s = closure; + upb_inttable *ref2 = &s->ref2; + upb_value v; + bool removed; + int32_t newcount; + + assert(obj == s->obj); + assert(subobj); + removed = upb_inttable_removeptr(ref2, subobj, &v); + /* The following assertion will fail if the visit() function visits a subobj + * that it did not have a ref2 on, or visits the same subobj too many times. */ + assert(removed); + newcount = upb_value_getint32(v) - 1; + if (newcount > 0) { + upb_inttable_insert(ref2, (uintptr_t)subobj, upb_value_int32(newcount)); + } +} + +static void visit(const upb_refcounted *r, upb_refcounted_visit *v, + void *closure) { + bool ok; + + /* In DEBUG_REFS mode we know what existing ref2 refs there are, so we know + * exactly the set of nodes that visit() should visit. So we verify visit()'s + * correctness here. */ + check_state state; + state.obj = r; + ok = upb_inttable_init(&state.ref2, UPB_CTYPE_INT32); + CHECK_OOM(ok); + getref2s(r, &state.ref2); + + /* This should visit any children in the ref2 table. */ + if (r->vtbl->visit) r->vtbl->visit(r, visit_check, &state); + + /* This assertion will fail if the visit() function missed any children. */ + assert(upb_inttable_count(&state.ref2) == 0); + upb_inttable_uninit(&state.ref2); + if (r->vtbl->visit) r->vtbl->visit(r, v, closure); +} + +static bool trackinit(upb_refcounted *r) { + r->refs = malloc(sizeof(*r->refs)); + r->ref2s = malloc(sizeof(*r->ref2s)); + if (!r->refs || !r->ref2s) goto err1; + + if (!upb_inttable_init(r->refs, UPB_CTYPE_PTR)) goto err1; + if (!upb_inttable_init(r->ref2s, UPB_CTYPE_PTR)) goto err2; + return true; + +err2: + upb_inttable_uninit(r->refs); +err1: + free(r->refs); + free(r->ref2s); + return false; +} + +static void trackfree(const upb_refcounted *r) { + upb_inttable_uninit(r->refs); + upb_inttable_uninit(r->ref2s); + free(r->refs); + free(r->ref2s); +} + +#else + +static void track(const upb_refcounted *r, const void *owner, bool ref2) { + UPB_UNUSED(r); + UPB_UNUSED(owner); + UPB_UNUSED(ref2); +} + +static void untrack(const upb_refcounted *r, const void *owner, bool ref2) { + UPB_UNUSED(r); + UPB_UNUSED(owner); + UPB_UNUSED(ref2); +} + +static void checkref(const upb_refcounted *r, const void *owner, bool ref2) { + UPB_UNUSED(r); + UPB_UNUSED(owner); + UPB_UNUSED(ref2); +} + +static bool trackinit(upb_refcounted *r) { + UPB_UNUSED(r); + return true; +} + +static void trackfree(const upb_refcounted *r) { + UPB_UNUSED(r); +} + +static void visit(const upb_refcounted *r, upb_refcounted_visit *v, + void *closure) { + if (r->vtbl->visit) r->vtbl->visit(r, v, closure); +} + +#endif /* UPB_DEBUG_REFS */ + + +/* freeze() *******************************************************************/ + +/* The freeze() operation is by far the most complicated part of this scheme. + * We compute strongly-connected components and then mutate the graph such that + * we preserve the invariants documented at the top of this file. And we must + * handle out-of-memory errors gracefully (without leaving the graph + * inconsistent), which adds to the fun. */ + +/* The state used by the freeze operation (shared across many functions). */ +typedef struct { + int depth; + int maxdepth; + uint64_t index; + /* Maps upb_refcounted* -> attributes (color, etc). attr layout varies by + * color. */ + upb_inttable objattr; + upb_inttable stack; /* stack of upb_refcounted* for Tarjan's algorithm. */ + upb_inttable groups; /* array of uint32_t*, malloc'd refcounts for new groups */ + upb_status *status; + jmp_buf err; +} tarjan; + +static void release_ref2(const upb_refcounted *obj, + const upb_refcounted *subobj, + void *closure); + +/* Node attributes -----------------------------------------------------------*/ + +/* After our analysis phase all nodes will be either GRAY or WHITE. */ + +typedef enum { + BLACK = 0, /* Object has not been seen. */ + GRAY, /* Object has been found via a refgroup but may not be reachable. */ + GREEN, /* Object is reachable and is currently on the Tarjan stack. */ + WHITE /* Object is reachable and has been assigned a group (SCC). */ +} color_t; + +UPB_NORETURN static void err(tarjan *t) { longjmp(t->err, 1); } +UPB_NORETURN static void oom(tarjan *t) { + upb_status_seterrmsg(t->status, "out of memory"); + err(t); +} + +static uint64_t trygetattr(const tarjan *t, const upb_refcounted *r) { + upb_value v; + return upb_inttable_lookupptr(&t->objattr, r, &v) ? + upb_value_getuint64(v) : 0; +} + +static uint64_t getattr(const tarjan *t, const upb_refcounted *r) { + upb_value v; + bool found = upb_inttable_lookupptr(&t->objattr, r, &v); + UPB_ASSERT_VAR(found, found); + return upb_value_getuint64(v); +} + +static void setattr(tarjan *t, const upb_refcounted *r, uint64_t attr) { + upb_inttable_removeptr(&t->objattr, r, NULL); + upb_inttable_insertptr(&t->objattr, r, upb_value_uint64(attr)); +} + +static color_t color(tarjan *t, const upb_refcounted *r) { + return trygetattr(t, r) & 0x3; /* Color is always stored in the low 2 bits. */ +} + +static void set_gray(tarjan *t, const upb_refcounted *r) { + assert(color(t, r) == BLACK); + setattr(t, r, GRAY); +} + +/* Pushes an obj onto the Tarjan stack and sets it to GREEN. */ +static void push(tarjan *t, const upb_refcounted *r) { + assert(color(t, r) == BLACK || color(t, r) == GRAY); + /* This defines the attr layout for the GREEN state. "index" and "lowlink" + * get 31 bits, which is plenty (limit of 2B objects frozen at a time). */ + setattr(t, r, GREEN | (t->index << 2) | (t->index << 33)); + if (++t->index == 0x80000000) { + upb_status_seterrmsg(t->status, "too many objects to freeze"); + err(t); + } + upb_inttable_push(&t->stack, upb_value_ptr((void*)r)); +} + +/* Pops an obj from the Tarjan stack and sets it to WHITE, with a ptr to its + * SCC group. */ +static upb_refcounted *pop(tarjan *t) { + upb_refcounted *r = upb_value_getptr(upb_inttable_pop(&t->stack)); + assert(color(t, r) == GREEN); + /* This defines the attr layout for nodes in the WHITE state. + * Top of group stack is [group, NULL]; we point at group. */ + setattr(t, r, WHITE | (upb_inttable_count(&t->groups) - 2) << 8); + return r; +} + +static void tarjan_newgroup(tarjan *t) { + uint32_t *group = malloc(sizeof(*group)); + if (!group) oom(t); + /* Push group and empty group leader (we'll fill in leader later). */ + if (!upb_inttable_push(&t->groups, upb_value_ptr(group)) || + !upb_inttable_push(&t->groups, upb_value_ptr(NULL))) { + free(group); + oom(t); + } + *group = 0; +} + +static uint32_t idx(tarjan *t, const upb_refcounted *r) { + assert(color(t, r) == GREEN); + return (getattr(t, r) >> 2) & 0x7FFFFFFF; +} + +static uint32_t lowlink(tarjan *t, const upb_refcounted *r) { + if (color(t, r) == GREEN) { + return getattr(t, r) >> 33; + } else { + return UINT32_MAX; + } +} + +static void set_lowlink(tarjan *t, const upb_refcounted *r, uint32_t lowlink) { + assert(color(t, r) == GREEN); + setattr(t, r, ((uint64_t)lowlink << 33) | (getattr(t, r) & 0x1FFFFFFFF)); +} + +static uint32_t *group(tarjan *t, upb_refcounted *r) { + uint64_t groupnum; + upb_value v; + bool found; + + assert(color(t, r) == WHITE); + groupnum = getattr(t, r) >> 8; + found = upb_inttable_lookup(&t->groups, groupnum, &v); + UPB_ASSERT_VAR(found, found); + return upb_value_getptr(v); +} + +/* If the group leader for this object's group has not previously been set, + * the given object is assigned to be its leader. */ +static upb_refcounted *groupleader(tarjan *t, upb_refcounted *r) { + uint64_t leader_slot; + upb_value v; + bool found; + + assert(color(t, r) == WHITE); + leader_slot = (getattr(t, r) >> 8) + 1; + found = upb_inttable_lookup(&t->groups, leader_slot, &v); + UPB_ASSERT_VAR(found, found); + if (upb_value_getptr(v)) { + return upb_value_getptr(v); + } else { + upb_inttable_remove(&t->groups, leader_slot, NULL); + upb_inttable_insert(&t->groups, leader_slot, upb_value_ptr(r)); + return r; + } +} + + +/* Tarjan's algorithm --------------------------------------------------------*/ + +/* See: + * http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm */ +static void do_tarjan(const upb_refcounted *obj, tarjan *t); + +static void tarjan_visit(const upb_refcounted *obj, + const upb_refcounted *subobj, + void *closure) { + tarjan *t = closure; + if (++t->depth > t->maxdepth) { + upb_status_seterrf(t->status, "graph too deep to freeze (%d)", t->maxdepth); + err(t); + } else if (subobj->is_frozen || color(t, subobj) == WHITE) { + /* Do nothing: we don't want to visit or color already-frozen nodes, + * and WHITE nodes have already been assigned a SCC. */ + } else if (color(t, subobj) < GREEN) { + /* Subdef has not yet been visited; recurse on it. */ + do_tarjan(subobj, t); + set_lowlink(t, obj, UPB_MIN(lowlink(t, obj), lowlink(t, subobj))); + } else if (color(t, subobj) == GREEN) { + /* Subdef is in the stack and hence in the current SCC. */ + set_lowlink(t, obj, UPB_MIN(lowlink(t, obj), idx(t, subobj))); + } + --t->depth; +} + +static void do_tarjan(const upb_refcounted *obj, tarjan *t) { + if (color(t, obj) == BLACK) { + /* We haven't seen this object's group; mark the whole group GRAY. */ + const upb_refcounted *o = obj; + do { set_gray(t, o); } while ((o = o->next) != obj); + } + + push(t, obj); + visit(obj, tarjan_visit, t); + if (lowlink(t, obj) == idx(t, obj)) { + tarjan_newgroup(t); + while (pop(t) != obj) + ; + } +} + + +/* freeze() ------------------------------------------------------------------*/ + +static void crossref(const upb_refcounted *r, const upb_refcounted *subobj, + void *_t) { + tarjan *t = _t; + assert(color(t, r) > BLACK); + if (color(t, subobj) > BLACK && r->group != subobj->group) { + /* Previously this ref was not reflected in subobj->group because they + * were in the same group; now that they are split a ref must be taken. */ + refgroup(subobj->group); + } +} + +static bool freeze(upb_refcounted *const*roots, int n, upb_status *s, + int maxdepth) { + volatile bool ret = false; + int i; + upb_inttable_iter iter; + + /* We run in two passes so that we can allocate all memory before performing + * any mutation of the input -- this allows us to leave the input unchanged + * in the case of memory allocation failure. */ + tarjan t; + t.index = 0; + t.depth = 0; + t.maxdepth = maxdepth; + t.status = s; + if (!upb_inttable_init(&t.objattr, UPB_CTYPE_UINT64)) goto err1; + if (!upb_inttable_init(&t.stack, UPB_CTYPE_PTR)) goto err2; + if (!upb_inttable_init(&t.groups, UPB_CTYPE_PTR)) goto err3; + if (setjmp(t.err) != 0) goto err4; + + + for (i = 0; i < n; i++) { + if (color(&t, roots[i]) < GREEN) { + do_tarjan(roots[i], &t); + } + } + + /* If we've made it this far, no further errors are possible so it's safe to + * mutate the objects without risk of leaving them in an inconsistent state. */ + ret = true; + + /* The transformation that follows requires care. The preconditions are: + * - all objects in attr map are WHITE or GRAY, and are in mutable groups + * (groups of all mutable objs) + * - no ref2(to, from) refs have incremented count(to) if both "to" and + * "from" are in our attr map (this follows from invariants (2) and (3)) */ + + /* Pass 1: we remove WHITE objects from their mutable groups, and add them to + * new groups according to the SCC's we computed. These new groups will + * consist of only frozen objects. None will be immediately collectible, + * because WHITE objects are by definition reachable from one of "roots", + * which the caller must own refs on. */ + upb_inttable_begin(&iter, &t.objattr); + for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) { + upb_refcounted *obj = (upb_refcounted*)upb_inttable_iter_key(&iter); + /* Since removal from a singly-linked list requires access to the object's + * predecessor, we consider obj->next instead of obj for moving. With the + * while() loop we guarantee that we will visit every node's predecessor. + * Proof: + * 1. every node's predecessor is in our attr map. + * 2. though the loop body may change a node's predecessor, it will only + * change it to be the node we are currently operating on, so with a + * while() loop we guarantee ourselves the chance to remove each node. */ + while (color(&t, obj->next) == WHITE && + group(&t, obj->next) != obj->next->group) { + upb_refcounted *leader; + + /* Remove from old group. */ + upb_refcounted *move = obj->next; + if (obj == move) { + /* Removing the last object from a group. */ + assert(*obj->group == obj->individual_count); + free(obj->group); + } else { + obj->next = move->next; + /* This may decrease to zero; we'll collect GRAY objects (if any) that + * remain in the group in the third pass. */ + assert(*move->group >= move->individual_count); + *move->group -= move->individual_count; + } + + /* Add to new group. */ + leader = groupleader(&t, move); + if (move == leader) { + /* First object added to new group is its leader. */ + move->group = group(&t, move); + move->next = move; + *move->group = move->individual_count; + } else { + /* Group already has at least one object in it. */ + assert(leader->group == group(&t, move)); + move->group = group(&t, move); + move->next = leader->next; + leader->next = move; + *move->group += move->individual_count; + } + + move->is_frozen = true; + } + } + + /* Pass 2: GRAY and WHITE objects "obj" with ref2(to, obj) references must + * increment count(to) if group(obj) != group(to) (which could now be the + * case if "to" was just frozen). */ + upb_inttable_begin(&iter, &t.objattr); + for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) { + upb_refcounted *obj = (upb_refcounted*)upb_inttable_iter_key(&iter); + visit(obj, crossref, &t); + } + + /* Pass 3: GRAY objects are collected if their group's refcount dropped to + * zero when we removed its white nodes. This can happen if they had only + * been kept alive by virtue of sharing a group with an object that was just + * frozen. + * + * It is important that we do this last, since the GRAY object's free() + * function could call unref2() on just-frozen objects, which will decrement + * refs that were added in pass 2. */ + upb_inttable_begin(&iter, &t.objattr); + for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) { + upb_refcounted *obj = (upb_refcounted*)upb_inttable_iter_key(&iter); + if (obj->group == NULL || *obj->group == 0) { + if (obj->group) { + upb_refcounted *o; + + /* We eagerly free() the group's count (since we can't easily determine + * the group's remaining size it's the easiest way to ensure it gets + * done). */ + free(obj->group); + + /* Visit to release ref2's (done in a separate pass since release_ref2 + * depends on o->group being unmodified so it can test merged()). */ + o = obj; + do { visit(o, release_ref2, NULL); } while ((o = o->next) != obj); + + /* Mark "group" fields as NULL so we know to free the objects later in + * this loop, but also don't try to delete the group twice. */ + o = obj; + do { o->group = NULL; } while ((o = o->next) != obj); + } + freeobj(obj); + } + } + +err4: + if (!ret) { + upb_inttable_begin(&iter, &t.groups); + for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) + free(upb_value_getptr(upb_inttable_iter_value(&iter))); + } + upb_inttable_uninit(&t.groups); +err3: + upb_inttable_uninit(&t.stack); +err2: + upb_inttable_uninit(&t.objattr); +err1: + return ret; +} + + +/* Misc internal functions ***************************************************/ + +static bool merged(const upb_refcounted *r, const upb_refcounted *r2) { + return r->group == r2->group; +} + +static void merge(upb_refcounted *r, upb_refcounted *from) { + upb_refcounted *base; + upb_refcounted *tmp; + + if (merged(r, from)) return; + *r->group += *from->group; + free(from->group); + base = from; + + /* Set all refcount pointers in the "from" chain to the merged refcount. + * + * TODO(haberman): this linear algorithm can result in an overall O(n^2) bound + * if the user continuously extends a group by one object. Prevent this by + * using one of the techniques in this paper: + * ftp://www.ncedc.org/outgoing/geomorph/dino/orals/p245-tarjan.pdf */ + do { from->group = r->group; } while ((from = from->next) != base); + + /* Merge the two circularly linked lists by swapping their next pointers. */ + tmp = r->next; + r->next = base->next; + base->next = tmp; +} + +static void unref(const upb_refcounted *r); + +static void release_ref2(const upb_refcounted *obj, + const upb_refcounted *subobj, + void *closure) { + UPB_UNUSED(closure); + untrack(subobj, obj, true); + if (!merged(obj, subobj)) { + assert(subobj->is_frozen); + unref(subobj); + } +} + +static void unref(const upb_refcounted *r) { + if (unrefgroup(r->group)) { + const upb_refcounted *o; + + free(r->group); + + /* In two passes, since release_ref2 needs a guarantee that any subobjs + * are alive. */ + o = r; + do { visit(o, release_ref2, NULL); } while((o = o->next) != r); + + o = r; + do { + const upb_refcounted *next = o->next; + assert(o->is_frozen || o->individual_count == 0); + freeobj((upb_refcounted*)o); + o = next; + } while(o != r); + } +} + +static void freeobj(upb_refcounted *o) { + trackfree(o); + o->vtbl->free((upb_refcounted*)o); +} + + +/* Public interface ***********************************************************/ + +bool upb_refcounted_init(upb_refcounted *r, + const struct upb_refcounted_vtbl *vtbl, + const void *owner) { +#ifndef NDEBUG + /* Endianness check. This is unrelated to upb_refcounted, it's just a + * convenient place to put the check that we can be assured will run for + * basically every program using upb. */ + const int x = 1; +#ifdef UPB_BIG_ENDIAN + assert(*(char*)&x != 1); +#else + assert(*(char*)&x == 1); +#endif +#endif + + r->next = r; + r->vtbl = vtbl; + r->individual_count = 0; + r->is_frozen = false; + r->group = malloc(sizeof(*r->group)); + if (!r->group) return false; + *r->group = 0; + if (!trackinit(r)) { + free(r->group); + return false; + } + upb_refcounted_ref(r, owner); + return true; +} + +bool upb_refcounted_isfrozen(const upb_refcounted *r) { + return r->is_frozen; +} + +void upb_refcounted_ref(const upb_refcounted *r, const void *owner) { + track(r, owner, false); + if (!r->is_frozen) + ((upb_refcounted*)r)->individual_count++; + refgroup(r->group); +} + +void upb_refcounted_unref(const upb_refcounted *r, const void *owner) { + untrack(r, owner, false); + if (!r->is_frozen) + ((upb_refcounted*)r)->individual_count--; + unref(r); +} + +void upb_refcounted_ref2(const upb_refcounted *r, upb_refcounted *from) { + assert(!from->is_frozen); /* Non-const pointer implies this. */ + track(r, from, true); + if (r->is_frozen) { + refgroup(r->group); + } else { + merge((upb_refcounted*)r, from); + } +} + +void upb_refcounted_unref2(const upb_refcounted *r, upb_refcounted *from) { + assert(!from->is_frozen); /* Non-const pointer implies this. */ + untrack(r, from, true); + if (r->is_frozen) { + unref(r); + } else { + assert(merged(r, from)); + } +} + +void upb_refcounted_donateref( + const upb_refcounted *r, const void *from, const void *to) { + assert(from != to); + if (to != NULL) + upb_refcounted_ref(r, to); + if (from != NULL) + upb_refcounted_unref(r, from); +} + +void upb_refcounted_checkref(const upb_refcounted *r, const void *owner) { + checkref(r, owner, false); +} + +bool upb_refcounted_freeze(upb_refcounted *const*roots, int n, upb_status *s, + int maxdepth) { + int i; + for (i = 0; i < n; i++) { + assert(!roots[i]->is_frozen); + } + return freeze(roots, n, s, maxdepth); +} + + +#include + +/* Fallback implementation if the shim is not specialized by the JIT. */ +#define SHIM_WRITER(type, ctype) \ + bool upb_shim_set ## type (void *c, const void *hd, ctype val) { \ + uint8_t *m = c; \ + const upb_shim_data *d = hd; \ + if (d->hasbit > 0) \ + *(uint8_t*)&m[d->hasbit / 8] |= 1 << (d->hasbit % 8); \ + *(ctype*)&m[d->offset] = val; \ + return true; \ + } \ + +SHIM_WRITER(double, double) +SHIM_WRITER(float, float) +SHIM_WRITER(int32, int32_t) +SHIM_WRITER(int64, int64_t) +SHIM_WRITER(uint32, uint32_t) +SHIM_WRITER(uint64, uint64_t) +SHIM_WRITER(bool, bool) +#undef SHIM_WRITER + +bool upb_shim_set(upb_handlers *h, const upb_fielddef *f, size_t offset, + int32_t hasbit) { + upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; + bool ok; + + upb_shim_data *d = malloc(sizeof(*d)); + if (!d) return false; + d->offset = offset; + d->hasbit = hasbit; + + upb_handlerattr_sethandlerdata(&attr, d); + upb_handlerattr_setalwaysok(&attr, true); + upb_handlers_addcleanup(h, d, free); + +#define TYPE(u, l) \ + case UPB_TYPE_##u: \ + ok = upb_handlers_set##l(h, f, upb_shim_set##l, &attr); break; + + ok = false; + + switch (upb_fielddef_type(f)) { + TYPE(INT64, int64); + TYPE(INT32, int32); + TYPE(ENUM, int32); + TYPE(UINT64, uint64); + TYPE(UINT32, uint32); + TYPE(DOUBLE, double); + TYPE(FLOAT, float); + TYPE(BOOL, bool); + default: assert(false); break; + } +#undef TYPE + + upb_handlerattr_uninit(&attr); + return ok; +} + +const upb_shim_data *upb_shim_getdata(const upb_handlers *h, upb_selector_t s, + upb_fieldtype_t *type) { + upb_func *f = upb_handlers_gethandler(h, s); + + if ((upb_int64_handlerfunc*)f == upb_shim_setint64) { + *type = UPB_TYPE_INT64; + } else if ((upb_int32_handlerfunc*)f == upb_shim_setint32) { + *type = UPB_TYPE_INT32; + } else if ((upb_uint64_handlerfunc*)f == upb_shim_setuint64) { + *type = UPB_TYPE_UINT64; + } else if ((upb_uint32_handlerfunc*)f == upb_shim_setuint32) { + *type = UPB_TYPE_UINT32; + } else if ((upb_double_handlerfunc*)f == upb_shim_setdouble) { + *type = UPB_TYPE_DOUBLE; + } else if ((upb_float_handlerfunc*)f == upb_shim_setfloat) { + *type = UPB_TYPE_FLOAT; + } else if ((upb_bool_handlerfunc*)f == upb_shim_setbool) { + *type = UPB_TYPE_BOOL; + } else { + return NULL; + } + + return (const upb_shim_data*)upb_handlers_gethandlerdata(h, s); +} + + +#include +#include + +static void upb_symtab_free(upb_refcounted *r) { + upb_symtab *s = (upb_symtab*)r; + upb_strtable_iter i; + upb_strtable_begin(&i, &s->symtab); + for (; !upb_strtable_done(&i); upb_strtable_next(&i)) { + const upb_def *def = upb_value_getptr(upb_strtable_iter_value(&i)); + upb_def_unref(def, s); + } + upb_strtable_uninit(&s->symtab); + free(s); +} + + +upb_symtab *upb_symtab_new(const void *owner) { + static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_symtab_free}; + upb_symtab *s = malloc(sizeof(*s)); + upb_refcounted_init(upb_symtab_upcast_mutable(s), &vtbl, owner); + upb_strtable_init(&s->symtab, UPB_CTYPE_PTR); + return s; +} + +void upb_symtab_freeze(upb_symtab *s) { + upb_refcounted *r; + bool ok; + + assert(!upb_symtab_isfrozen(s)); + r = upb_symtab_upcast_mutable(s); + /* The symtab does not take ref2's (see refcounted.h) on the defs, because + * defs cannot refer back to the table and therefore cannot create cycles. So + * 0 will suffice for maxdepth here. */ + ok = upb_refcounted_freeze(&r, 1, NULL, 0); + UPB_ASSERT_VAR(ok, ok); +} + +const upb_def *upb_symtab_lookup(const upb_symtab *s, const char *sym) { + upb_value v; + upb_def *ret = upb_strtable_lookup(&s->symtab, sym, &v) ? + upb_value_getptr(v) : NULL; + return ret; +} + +const upb_msgdef *upb_symtab_lookupmsg(const upb_symtab *s, const char *sym) { + upb_value v; + upb_def *def = upb_strtable_lookup(&s->symtab, sym, &v) ? + upb_value_getptr(v) : NULL; + return def ? upb_dyncast_msgdef(def) : NULL; +} + +const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym) { + upb_value v; + upb_def *def = upb_strtable_lookup(&s->symtab, sym, &v) ? + upb_value_getptr(v) : NULL; + return def ? upb_dyncast_enumdef(def) : NULL; +} + +/* Given a symbol and the base symbol inside which it is defined, find the + * symbol's definition in t. */ +static upb_def *upb_resolvename(const upb_strtable *t, + const char *base, const char *sym) { + if(strlen(sym) == 0) return NULL; + if(sym[0] == '.') { + /* Symbols starting with '.' are absolute, so we do a single lookup. + * Slice to omit the leading '.' */ + upb_value v; + return upb_strtable_lookup(t, sym + 1, &v) ? upb_value_getptr(v) : NULL; + } else { + /* Remove components from base until we find an entry or run out. + * TODO: This branch is totally broken, but currently not used. */ + (void)base; + assert(false); + return NULL; + } +} + +const upb_def *upb_symtab_resolve(const upb_symtab *s, const char *base, + const char *sym) { + upb_def *ret = upb_resolvename(&s->symtab, base, sym); + return ret; +} + +/* Starts a depth-first traversal at "def", recursing into any subdefs + * (ie. submessage types). Adds duplicates of existing defs to addtab + * wherever necessary, so that the resulting symtab will be consistent once + * addtab is added. + * + * More specifically, if any def D is found in the DFS that: + * + * 1. can reach a def that is being replaced by something in addtab, AND + * + * 2. is not itself being replaced already (ie. this name doesn't already + * exist in addtab) + * + * ...then a duplicate (new copy) of D will be added to addtab. + * + * Returns true if this happened for any def reachable from "def." + * + * It is slightly tricky to do this correctly in the presence of cycles. If we + * detect that our DFS has hit a cycle, we might not yet know if any SCCs on + * our stack can reach a def in addtab or not. Once we figure this out, that + * answer needs to apply to *all* defs in these SCCs, even if we visited them + * already. So a straight up one-pass cycle-detecting DFS won't work. + * + * To work around this problem, we traverse each SCC (which we already + * computed, since these defs are frozen) as a single node. We first compute + * whether the SCC as a whole can reach any def in addtab, then we dup (or not) + * the entire SCC. This requires breaking the encapsulation of upb_refcounted, + * since that is where we get the data about what SCC we are in. */ +static bool upb_resolve_dfs(const upb_def *def, upb_strtable *addtab, + const void *new_owner, upb_inttable *seen, + upb_status *s) { + upb_value v; + bool need_dup; + const upb_def *base; + const void* memoize_key; + + /* Memoize results of this function for efficiency (since we're traversing a + * DAG this is not needed to limit the depth of the search). + * + * We memoize by SCC instead of by individual def. */ + memoize_key = def->base.group; + + if (upb_inttable_lookupptr(seen, memoize_key, &v)) + return upb_value_getbool(v); + + /* Visit submessages for all messages in the SCC. */ + need_dup = false; + base = def; + do { + upb_value v; + const upb_msgdef *m; + + assert(upb_def_isfrozen(def)); + if (def->type == UPB_DEF_FIELD) continue; + if (upb_strtable_lookup(addtab, upb_def_fullname(def), &v)) { + need_dup = true; + } + + /* For messages, continue the recursion by visiting all subdefs, but only + * ones in different SCCs. */ + m = upb_dyncast_msgdef(def); + if (m) { + upb_msg_field_iter i; + for(upb_msg_field_begin(&i, m); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { + upb_fielddef *f = upb_msg_iter_field(&i); + const upb_def *subdef; + + if (!upb_fielddef_hassubdef(f)) continue; + subdef = upb_fielddef_subdef(f); + + /* Skip subdefs in this SCC. */ + if (def->base.group == subdef->base.group) continue; + + /* |= to avoid short-circuit; we need its side-effects. */ + need_dup |= upb_resolve_dfs(subdef, addtab, new_owner, seen, s); + if (!upb_ok(s)) return false; + } + } + } while ((def = (upb_def*)def->base.next) != base); + + if (need_dup) { + /* Dup all defs in this SCC that don't already have entries in addtab. */ + def = base; + do { + const char *name; + + if (def->type == UPB_DEF_FIELD) continue; + name = upb_def_fullname(def); + if (!upb_strtable_lookup(addtab, name, NULL)) { + upb_def *newdef = upb_def_dup(def, new_owner); + if (!newdef) goto oom; + newdef->came_from_user = false; + if (!upb_strtable_insert(addtab, name, upb_value_ptr(newdef))) + goto oom; + } + } while ((def = (upb_def*)def->base.next) != base); + } + + upb_inttable_insertptr(seen, memoize_key, upb_value_bool(need_dup)); + return need_dup; + +oom: + upb_status_seterrmsg(s, "out of memory"); + return false; +} + +/* TODO(haberman): we need a lot more testing of error conditions. + * The came_from_user stuff in particular is not tested. */ +bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, + upb_status *status) { + int i; + upb_strtable_iter iter; + upb_def **add_defs = NULL; + upb_strtable addtab; + upb_inttable seen; + + assert(!upb_symtab_isfrozen(s)); + if (!upb_strtable_init(&addtab, UPB_CTYPE_PTR)) { + upb_status_seterrmsg(status, "out of memory"); + return false; + } + + /* Add new defs to our "add" set. */ + for (i = 0; i < n; i++) { + upb_def *def = defs[i]; + const char *fullname; + upb_fielddef *f; + + if (upb_def_isfrozen(def)) { + upb_status_seterrmsg(status, "added defs must be mutable"); + goto err; + } + assert(!upb_def_isfrozen(def)); + fullname = upb_def_fullname(def); + if (!fullname) { + upb_status_seterrmsg( + status, "Anonymous defs cannot be added to a symtab"); + goto err; + } + + f = upb_dyncast_fielddef_mutable(def); + + if (f) { + if (!upb_fielddef_containingtypename(f)) { + upb_status_seterrmsg(status, + "Standalone fielddefs must have a containing type " + "(extendee) name set"); + goto err; + } + } else { + if (upb_strtable_lookup(&addtab, fullname, NULL)) { + upb_status_seterrf(status, "Conflicting defs named '%s'", fullname); + goto err; + } + /* We need this to back out properly, because if there is a failure we + * need to donate the ref back to the caller. */ + def->came_from_user = true; + upb_def_donateref(def, ref_donor, s); + if (!upb_strtable_insert(&addtab, fullname, upb_value_ptr(def))) + goto oom_err; + } + } + + /* Add standalone fielddefs (ie. extensions) to the appropriate messages. + * If the appropriate message only exists in the existing symtab, duplicate + * it so we have a mutable copy we can add the fields to. */ + for (i = 0; i < n; i++) { + upb_def *def = defs[i]; + upb_fielddef *f = upb_dyncast_fielddef_mutable(def); + const char *msgname; + upb_value v; + upb_msgdef *m; + + if (!f) continue; + msgname = upb_fielddef_containingtypename(f); + /* We validated this earlier in this function. */ + assert(msgname); + + /* If the extendee name is absolutely qualified, move past the initial ".". + * TODO(haberman): it is not obvious what it would mean if this was not + * absolutely qualified. */ + if (msgname[0] == '.') { + msgname++; + } + + if (upb_strtable_lookup(&addtab, msgname, &v)) { + /* Extendee is in the set of defs the user asked us to add. */ + m = upb_value_getptr(v); + } else { + /* Need to find and dup the extendee from the existing symtab. */ + const upb_msgdef *frozen_m = upb_symtab_lookupmsg(s, msgname); + if (!frozen_m) { + upb_status_seterrf(status, + "Tried to extend message %s that does not exist " + "in this SymbolTable.", + msgname); + goto err; + } + m = upb_msgdef_dup(frozen_m, s); + if (!m) goto oom_err; + if (!upb_strtable_insert(&addtab, msgname, upb_value_ptr(m))) { + upb_msgdef_unref(m, s); + goto oom_err; + } + } + + if (!upb_msgdef_addfield(m, f, ref_donor, status)) { + goto err; + } + } + + /* Add dups of any existing def that can reach a def with the same name as + * anything in our "add" set. */ + if (!upb_inttable_init(&seen, UPB_CTYPE_BOOL)) goto oom_err; + upb_strtable_begin(&iter, &s->symtab); + for (; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { + upb_def *def = upb_value_getptr(upb_strtable_iter_value(&iter)); + upb_resolve_dfs(def, &addtab, s, &seen, status); + if (!upb_ok(status)) goto err; + } + upb_inttable_uninit(&seen); + + /* Now using the table, resolve symbolic references for subdefs. */ + upb_strtable_begin(&iter, &addtab); + for (; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { + const char *base; + upb_def *def = upb_value_getptr(upb_strtable_iter_value(&iter)); + upb_msgdef *m = upb_dyncast_msgdef_mutable(def); + upb_msg_field_iter j; + + if (!m) continue; + /* Type names are resolved relative to the message in which they appear. */ + base = upb_msgdef_fullname(m); + + for(upb_msg_field_begin(&j, m); + !upb_msg_field_done(&j); + upb_msg_field_next(&j)) { + upb_fielddef *f = upb_msg_iter_field(&j); + const char *name = upb_fielddef_subdefname(f); + if (name && !upb_fielddef_subdef(f)) { + /* Try the lookup in the current set of to-be-added defs first. If not + * there, try existing defs. */ + upb_def *subdef = upb_resolvename(&addtab, base, name); + if (subdef == NULL) { + subdef = upb_resolvename(&s->symtab, base, name); + } + if (subdef == NULL) { + upb_status_seterrf( + status, "couldn't resolve name '%s' in message '%s'", name, base); + goto err; + } else if (!upb_fielddef_setsubdef(f, subdef, status)) { + goto err; + } + } + } + } + + /* We need an array of the defs in addtab, for passing to upb_def_freeze. */ + add_defs = malloc(sizeof(void*) * upb_strtable_count(&addtab)); + if (add_defs == NULL) goto oom_err; + upb_strtable_begin(&iter, &addtab); + for (n = 0; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { + add_defs[n++] = upb_value_getptr(upb_strtable_iter_value(&iter)); + } + + if (!upb_def_freeze(add_defs, n, status)) goto err; + + /* This must be delayed until all errors have been detected, since error + * recovery code uses this table to cleanup defs. */ + upb_strtable_uninit(&addtab); + + /* TODO(haberman) we don't properly handle errors after this point (like + * OOM in upb_strtable_insert() below). */ + for (i = 0; i < n; i++) { + upb_def *def = add_defs[i]; + const char *name = upb_def_fullname(def); + upb_value v; + bool success; + + if (upb_strtable_remove(&s->symtab, name, &v)) { + const upb_def *def = upb_value_getptr(v); + upb_def_unref(def, s); + } + success = upb_strtable_insert(&s->symtab, name, upb_value_ptr(def)); + UPB_ASSERT_VAR(success, success == true); + } + free(add_defs); + return true; + +oom_err: + upb_status_seterrmsg(status, "out of memory"); +err: { + /* For defs the user passed in, we need to donate the refs back. For defs + * we dup'd, we need to just unref them. */ + upb_strtable_begin(&iter, &addtab); + for (; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { + upb_def *def = upb_value_getptr(upb_strtable_iter_value(&iter)); + bool came_from_user = def->came_from_user; + def->came_from_user = false; + if (came_from_user) { + upb_def_donateref(def, s, ref_donor); + } else { + upb_def_unref(def, s); + } + } + } + upb_strtable_uninit(&addtab); + free(add_defs); + assert(!upb_ok(status)); + return false; +} + +/* Iteration. */ + +static void advance_to_matching(upb_symtab_iter *iter) { + if (iter->type == UPB_DEF_ANY) + return; + + while (!upb_strtable_done(&iter->iter) && + iter->type != upb_symtab_iter_def(iter)->type) { + upb_strtable_next(&iter->iter); + } +} + +void upb_symtab_begin(upb_symtab_iter *iter, const upb_symtab *s, + upb_deftype_t type) { + upb_strtable_begin(&iter->iter, &s->symtab); + iter->type = type; + advance_to_matching(iter); +} + +void upb_symtab_next(upb_symtab_iter *iter) { + upb_strtable_next(&iter->iter); + advance_to_matching(iter); +} + +bool upb_symtab_done(const upb_symtab_iter *iter) { + return upb_strtable_done(&iter->iter); +} + +const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter) { + return upb_value_getptr(upb_strtable_iter_value(&iter->iter)); +} +/* +** upb_table Implementation +** +** Implementation is heavily inspired by Lua's ltable.c. +*/ + + +#include +#include + +#define UPB_MAXARRSIZE 16 /* 64k. */ + +/* From Chromium. */ +#define ARRAY_SIZE(x) \ + ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) + +static const double MAX_LOAD = 0.85; + +/* The minimum utilization of the array part of a mixed hash/array table. This + * is a speed/memory-usage tradeoff (though it's not straightforward because of + * cache effects). The lower this is, the more memory we'll use. */ +static const double MIN_DENSITY = 0.1; + +bool is_pow2(uint64_t v) { return v == 0 || (v & (v - 1)) == 0; } + +int log2ceil(uint64_t v) { + int ret = 0; + bool pow2 = is_pow2(v); + while (v >>= 1) ret++; + ret = pow2 ? ret : ret + 1; /* Ceiling. */ + return UPB_MIN(UPB_MAXARRSIZE, ret); +} + +char *upb_strdup(const char *s) { + return upb_strdup2(s, strlen(s)); +} + +char *upb_strdup2(const char *s, size_t len) { + size_t n; + char *p; + + /* Prevent overflow errors. */ + if (len == SIZE_MAX) return NULL; + /* Always null-terminate, even if binary data; but don't rely on the input to + * have a null-terminating byte since it may be a raw binary buffer. */ + n = len + 1; + p = malloc(n); + if (p) { + memcpy(p, s, len); + p[len] = 0; + } + return p; +} + +/* A type to represent the lookup key of either a strtable or an inttable. */ +typedef union { + uintptr_t num; + struct { + const char *str; + size_t len; + } str; +} lookupkey_t; + +static lookupkey_t strkey2(const char *str, size_t len) { + lookupkey_t k; + k.str.str = str; + k.str.len = len; + return k; +} + +static lookupkey_t intkey(uintptr_t key) { + lookupkey_t k; + k.num = key; + return k; +} + +typedef uint32_t hashfunc_t(upb_tabkey key); +typedef bool eqlfunc_t(upb_tabkey k1, lookupkey_t k2); + +/* Base table (shared code) ***************************************************/ + +/* For when we need to cast away const. */ +static upb_tabent *mutable_entries(upb_table *t) { + return (upb_tabent*)t->entries; +} + +static bool isfull(upb_table *t) { + return (double)(t->count + 1) / upb_table_size(t) > MAX_LOAD; +} + +static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2) { + size_t bytes; + + t->count = 0; + t->ctype = ctype; + t->size_lg2 = size_lg2; + t->mask = upb_table_size(t) ? upb_table_size(t) - 1 : 0; + bytes = upb_table_size(t) * sizeof(upb_tabent); + if (bytes > 0) { + t->entries = malloc(bytes); + if (!t->entries) return false; + memset(mutable_entries(t), 0, bytes); + } else { + t->entries = NULL; + } + return true; +} + +static void uninit(upb_table *t) { free(mutable_entries(t)); } + +static upb_tabent *emptyent(upb_table *t) { + upb_tabent *e = mutable_entries(t) + upb_table_size(t); + while (1) { if (upb_tabent_isempty(--e)) return e; assert(e > t->entries); } +} + +static upb_tabent *getentry_mutable(upb_table *t, uint32_t hash) { + return (upb_tabent*)upb_getentry(t, hash); +} + +static const upb_tabent *findentry(const upb_table *t, lookupkey_t key, + uint32_t hash, eqlfunc_t *eql) { + const upb_tabent *e; + + if (t->size_lg2 == 0) return NULL; + e = upb_getentry(t, hash); + if (upb_tabent_isempty(e)) return NULL; + while (1) { + if (eql(e->key, key)) return e; + if ((e = e->next) == NULL) return NULL; + } +} + +static upb_tabent *findentry_mutable(upb_table *t, lookupkey_t key, + uint32_t hash, eqlfunc_t *eql) { + return (upb_tabent*)findentry(t, key, hash, eql); +} + +static bool lookup(const upb_table *t, lookupkey_t key, upb_value *v, + uint32_t hash, eqlfunc_t *eql) { + const upb_tabent *e = findentry(t, key, hash, eql); + if (e) { + if (v) { + _upb_value_setval(v, e->val.val, t->ctype); + } + return true; + } else { + return false; + } +} + +/* The given key must not already exist in the table. */ +static void insert(upb_table *t, lookupkey_t key, upb_tabkey tabkey, + upb_value val, uint32_t hash, + hashfunc_t *hashfunc, eqlfunc_t *eql) { + upb_tabent *mainpos_e; + upb_tabent *our_e; + + UPB_UNUSED(eql); + UPB_UNUSED(key); + assert(findentry(t, key, hash, eql) == NULL); + assert(val.ctype == t->ctype); + + t->count++; + mainpos_e = getentry_mutable(t, hash); + our_e = mainpos_e; + + if (upb_tabent_isempty(mainpos_e)) { + /* Our main position is empty; use it. */ + our_e->next = NULL; + } else { + /* Collision. */ + upb_tabent *new_e = emptyent(t); + /* Head of collider's chain. */ + upb_tabent *chain = getentry_mutable(t, hashfunc(mainpos_e->key)); + if (chain == mainpos_e) { + /* Existing ent is in its main posisiton (it has the same hash as us, and + * is the head of our chain). Insert to new ent and append to this chain. */ + new_e->next = mainpos_e->next; + mainpos_e->next = new_e; + our_e = new_e; + } else { + /* Existing ent is not in its main position (it is a node in some other + * chain). This implies that no existing ent in the table has our hash. + * Evict it (updating its chain) and use its ent for head of our chain. */ + *new_e = *mainpos_e; /* copies next. */ + while (chain->next != mainpos_e) { + chain = (upb_tabent*)chain->next; + assert(chain); + } + chain->next = new_e; + our_e = mainpos_e; + our_e->next = NULL; + } + } + our_e->key = tabkey; + our_e->val.val = val.val; + assert(findentry(t, key, hash, eql) == our_e); +} + +static bool rm(upb_table *t, lookupkey_t key, upb_value *val, + upb_tabkey *removed, uint32_t hash, eqlfunc_t *eql) { + upb_tabent *chain = getentry_mutable(t, hash); + if (upb_tabent_isempty(chain)) return false; + if (eql(chain->key, key)) { + /* Element to remove is at the head of its chain. */ + t->count--; + if (val) { + _upb_value_setval(val, chain->val.val, t->ctype); + } + if (chain->next) { + upb_tabent *move = (upb_tabent*)chain->next; + *chain = *move; + if (removed) *removed = move->key; + move->key = 0; /* Make the slot empty. */ + } else { + if (removed) *removed = chain->key; + chain->key = 0; /* Make the slot empty. */ + } + return true; + } else { + /* Element to remove is either in a non-head position or not in the + * table. */ + while (chain->next && !eql(chain->next->key, key)) + chain = (upb_tabent*)chain->next; + if (chain->next) { + /* Found element to remove. */ + upb_tabent *rm; + + if (val) { + _upb_value_setval(val, chain->next->val.val, t->ctype); + } + rm = (upb_tabent*)chain->next; + if (removed) *removed = rm->key; + rm->key = 0; + chain->next = rm->next; + t->count--; + return true; + } else { + return false; + } + } +} + +static size_t next(const upb_table *t, size_t i) { + do { + if (++i >= upb_table_size(t)) + return SIZE_MAX; + } while(upb_tabent_isempty(&t->entries[i])); + + return i; +} + +static size_t begin(const upb_table *t) { + return next(t, -1); +} + + +/* upb_strtable ***************************************************************/ + +/* A simple "subclass" of upb_table that only adds a hash function for strings. */ + +static upb_tabkey strcopy(lookupkey_t k2) { + char *str = malloc(k2.str.len + sizeof(uint32_t) + 1); + if (str == NULL) return 0; + memcpy(str, &k2.str.len, sizeof(uint32_t)); + memcpy(str + sizeof(uint32_t), k2.str.str, k2.str.len + 1); + return (uintptr_t)str; +} + +static uint32_t strhash(upb_tabkey key) { + uint32_t len; + char *str = upb_tabstr(key, &len); + return MurmurHash2(str, len, 0); +} + +static bool streql(upb_tabkey k1, lookupkey_t k2) { + uint32_t len; + char *str = upb_tabstr(k1, &len); + return len == k2.str.len && memcmp(str, k2.str.str, len) == 0; +} + +bool upb_strtable_init(upb_strtable *t, upb_ctype_t ctype) { + return init(&t->t, ctype, 2); +} + +void upb_strtable_uninit(upb_strtable *t) { + size_t i; + for (i = 0; i < upb_table_size(&t->t); i++) + free((void*)t->t.entries[i].key); + uninit(&t->t); +} + +bool upb_strtable_resize(upb_strtable *t, size_t size_lg2) { + upb_strtable new_table; + upb_strtable_iter i; + + if (!init(&new_table.t, t->t.ctype, size_lg2)) + return false; + upb_strtable_begin(&i, t); + for ( ; !upb_strtable_done(&i); upb_strtable_next(&i)) { + upb_strtable_insert2( + &new_table, + upb_strtable_iter_key(&i), + upb_strtable_iter_keylength(&i), + upb_strtable_iter_value(&i)); + } + upb_strtable_uninit(t); + *t = new_table; + return true; +} + +bool upb_strtable_insert2(upb_strtable *t, const char *k, size_t len, + upb_value v) { + lookupkey_t key; + upb_tabkey tabkey; + uint32_t hash; + + if (isfull(&t->t)) { + /* Need to resize. New table of double the size, add old elements to it. */ + if (!upb_strtable_resize(t, t->t.size_lg2 + 1)) { + return false; + } + } + + key = strkey2(k, len); + tabkey = strcopy(key); + if (tabkey == 0) return false; + + hash = MurmurHash2(key.str.str, key.str.len, 0); + insert(&t->t, key, tabkey, v, hash, &strhash, &streql); + return true; +} + +bool upb_strtable_lookup2(const upb_strtable *t, const char *key, size_t len, + upb_value *v) { + uint32_t hash = MurmurHash2(key, len, 0); + return lookup(&t->t, strkey2(key, len), v, hash, &streql); +} + +bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len, + upb_value *val) { + uint32_t hash = MurmurHash2(key, strlen(key), 0); + upb_tabkey tabkey; + if (rm(&t->t, strkey2(key, len), val, &tabkey, hash, &streql)) { + free((void*)tabkey); + return true; + } else { + return false; + } +} + +/* Iteration */ + +static const upb_tabent *str_tabent(const upb_strtable_iter *i) { + return &i->t->t.entries[i->index]; +} + +void upb_strtable_begin(upb_strtable_iter *i, const upb_strtable *t) { + i->t = t; + i->index = begin(&t->t); +} + +void upb_strtable_next(upb_strtable_iter *i) { + i->index = next(&i->t->t, i->index); +} + +bool upb_strtable_done(const upb_strtable_iter *i) { + return i->index >= upb_table_size(&i->t->t) || + upb_tabent_isempty(str_tabent(i)); +} + +const char *upb_strtable_iter_key(upb_strtable_iter *i) { + assert(!upb_strtable_done(i)); + return upb_tabstr(str_tabent(i)->key, NULL); +} + +size_t upb_strtable_iter_keylength(upb_strtable_iter *i) { + uint32_t len; + assert(!upb_strtable_done(i)); + upb_tabstr(str_tabent(i)->key, &len); + return len; +} + +upb_value upb_strtable_iter_value(const upb_strtable_iter *i) { + assert(!upb_strtable_done(i)); + return _upb_value_val(str_tabent(i)->val.val, i->t->t.ctype); +} + +void upb_strtable_iter_setdone(upb_strtable_iter *i) { + i->index = SIZE_MAX; +} + +bool upb_strtable_iter_isequal(const upb_strtable_iter *i1, + const upb_strtable_iter *i2) { + if (upb_strtable_done(i1) && upb_strtable_done(i2)) + return true; + return i1->t == i2->t && i1->index == i2->index; +} + + +/* upb_inttable ***************************************************************/ + +/* For inttables we use a hybrid structure where small keys are kept in an + * array and large keys are put in the hash table. */ + +static uint32_t inthash(upb_tabkey key) { return upb_inthash(key); } + +static bool inteql(upb_tabkey k1, lookupkey_t k2) { + return k1 == k2.num; +} + +static upb_tabval *mutable_array(upb_inttable *t) { + return (upb_tabval*)t->array; +} + +static upb_tabval *inttable_val(upb_inttable *t, uintptr_t key) { + if (key < t->array_size) { + return upb_arrhas(t->array[key]) ? &(mutable_array(t)[key]) : NULL; + } else { + upb_tabent *e = + findentry_mutable(&t->t, intkey(key), upb_inthash(key), &inteql); + return e ? &e->val : NULL; + } +} + +static const upb_tabval *inttable_val_const(const upb_inttable *t, + uintptr_t key) { + return inttable_val((upb_inttable*)t, key); +} + +size_t upb_inttable_count(const upb_inttable *t) { + return t->t.count + t->array_count; +} + +static void check(upb_inttable *t) { + UPB_UNUSED(t); +#if defined(UPB_DEBUG_TABLE) && !defined(NDEBUG) + { + /* This check is very expensive (makes inserts/deletes O(N)). */ + size_t count = 0; + upb_inttable_iter i; + upb_inttable_begin(&i, t); + for(; !upb_inttable_done(&i); upb_inttable_next(&i), count++) { + assert(upb_inttable_lookup(t, upb_inttable_iter_key(&i), NULL)); + } + assert(count == upb_inttable_count(t)); + } +#endif +} + +bool upb_inttable_sizedinit(upb_inttable *t, upb_ctype_t ctype, + size_t asize, int hsize_lg2) { + size_t array_bytes; + + if (!init(&t->t, ctype, hsize_lg2)) return false; + /* Always make the array part at least 1 long, so that we know key 0 + * won't be in the hash part, which simplifies things. */ + t->array_size = UPB_MAX(1, asize); + t->array_count = 0; + array_bytes = t->array_size * sizeof(upb_value); + t->array = malloc(array_bytes); + if (!t->array) { + uninit(&t->t); + return false; + } + memset(mutable_array(t), 0xff, array_bytes); + check(t); + return true; +} + +bool upb_inttable_init(upb_inttable *t, upb_ctype_t ctype) { + return upb_inttable_sizedinit(t, ctype, 0, 4); +} + +void upb_inttable_uninit(upb_inttable *t) { + uninit(&t->t); + free(mutable_array(t)); +} + +bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) { + /* XXX: Table can't store value (uint64_t)-1. Need to somehow statically + * guarantee that this is not necessary, or fix the limitation. */ + upb_tabval tabval; + tabval.val = val.val; + UPB_UNUSED(tabval); + assert(upb_arrhas(tabval)); + + if (key < t->array_size) { + assert(!upb_arrhas(t->array[key])); + t->array_count++; + mutable_array(t)[key].val = val.val; + } else { + if (isfull(&t->t)) { + /* Need to resize the hash part, but we re-use the array part. */ + size_t i; + upb_table new_table; + if (!init(&new_table, t->t.ctype, t->t.size_lg2 + 1)) + return false; + for (i = begin(&t->t); i < upb_table_size(&t->t); i = next(&t->t, i)) { + const upb_tabent *e = &t->t.entries[i]; + uint32_t hash; + upb_value v; + + _upb_value_setval(&v, e->val.val, t->t.ctype); + hash = upb_inthash(e->key); + insert(&new_table, intkey(e->key), e->key, v, hash, &inthash, &inteql); + } + + assert(t->t.count == new_table.count); + + uninit(&t->t); + t->t = new_table; + } + insert(&t->t, intkey(key), key, val, upb_inthash(key), &inthash, &inteql); + } + check(t); + return true; +} + +bool upb_inttable_lookup(const upb_inttable *t, uintptr_t key, upb_value *v) { + const upb_tabval *table_v = inttable_val_const(t, key); + if (!table_v) return false; + if (v) _upb_value_setval(v, table_v->val, t->t.ctype); + return true; +} + +bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val) { + upb_tabval *table_v = inttable_val(t, key); + if (!table_v) return false; + table_v->val = val.val; + return true; +} + +bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val) { + bool success; + if (key < t->array_size) { + if (upb_arrhas(t->array[key])) { + upb_tabval empty = UPB_TABVALUE_EMPTY_INIT; + t->array_count--; + if (val) { + _upb_value_setval(val, t->array[key].val, t->t.ctype); + } + mutable_array(t)[key] = empty; + success = true; + } else { + success = false; + } + } else { + upb_tabkey removed; + uint32_t hash = upb_inthash(key); + success = rm(&t->t, intkey(key), val, &removed, hash, &inteql); + } + check(t); + return success; +} + +bool upb_inttable_push(upb_inttable *t, upb_value val) { + return upb_inttable_insert(t, upb_inttable_count(t), val); +} + +upb_value upb_inttable_pop(upb_inttable *t) { + upb_value val; + bool ok = upb_inttable_remove(t, upb_inttable_count(t) - 1, &val); + UPB_ASSERT_VAR(ok, ok); + return val; +} + +bool upb_inttable_insertptr(upb_inttable *t, const void *key, upb_value val) { + return upb_inttable_insert(t, (uintptr_t)key, val); +} + +bool upb_inttable_lookupptr(const upb_inttable *t, const void *key, + upb_value *v) { + return upb_inttable_lookup(t, (uintptr_t)key, v); +} + +bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val) { + return upb_inttable_remove(t, (uintptr_t)key, val); +} + +void upb_inttable_compact(upb_inttable *t) { + /* Create a power-of-two histogram of the table keys. */ + int counts[UPB_MAXARRSIZE + 1] = {0}; + uintptr_t max_key = 0; + upb_inttable_iter i; + size_t arr_size; + int arr_count; + upb_inttable new_t; + + upb_inttable_begin(&i, t); + for (; !upb_inttable_done(&i); upb_inttable_next(&i)) { + uintptr_t key = upb_inttable_iter_key(&i); + if (key > max_key) { + max_key = key; + } + counts[log2ceil(key)]++; + } + + arr_size = 1; + arr_count = upb_inttable_count(t); + + if (upb_inttable_count(t) >= max_key * MIN_DENSITY) { + /* We can put 100% of the entries in the array part. */ + arr_size = max_key + 1; + } else { + /* Find the largest power of two that satisfies the MIN_DENSITY + * definition. */ + int size_lg2; + for (size_lg2 = ARRAY_SIZE(counts) - 1; size_lg2 > 1; size_lg2--) { + arr_size = 1 << size_lg2; + arr_count -= counts[size_lg2]; + if (arr_count >= arr_size * MIN_DENSITY) { + break; + } + } + } + + /* Array part must always be at least 1 entry large to catch lookups of key + * 0. Key 0 must always be in the array part because "0" in the hash part + * denotes an empty entry. */ + arr_size = UPB_MAX(arr_size, 1); + + { + /* Insert all elements into new, perfectly-sized table. */ + int hash_count = upb_inttable_count(t) - arr_count; + int hash_size = hash_count ? (hash_count / MAX_LOAD) + 1 : 0; + int hashsize_lg2 = log2ceil(hash_size); + + assert(hash_count >= 0); + upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2); + upb_inttable_begin(&i, t); + for (; !upb_inttable_done(&i); upb_inttable_next(&i)) { + uintptr_t k = upb_inttable_iter_key(&i); + upb_inttable_insert(&new_t, k, upb_inttable_iter_value(&i)); + } + assert(new_t.array_size == arr_size); + assert(new_t.t.size_lg2 == hashsize_lg2); + } + upb_inttable_uninit(t); + *t = new_t; +} + +/* Iteration. */ + +static const upb_tabent *int_tabent(const upb_inttable_iter *i) { + assert(!i->array_part); + return &i->t->t.entries[i->index]; +} + +static upb_tabval int_arrent(const upb_inttable_iter *i) { + assert(i->array_part); + return i->t->array[i->index]; +} + +void upb_inttable_begin(upb_inttable_iter *i, const upb_inttable *t) { + i->t = t; + i->index = -1; + i->array_part = true; + upb_inttable_next(i); +} + +void upb_inttable_next(upb_inttable_iter *iter) { + const upb_inttable *t = iter->t; + if (iter->array_part) { + while (++iter->index < t->array_size) { + if (upb_arrhas(int_arrent(iter))) { + return; + } + } + iter->array_part = false; + iter->index = begin(&t->t); + } else { + iter->index = next(&t->t, iter->index); + } +} + +bool upb_inttable_done(const upb_inttable_iter *i) { + if (i->array_part) { + return i->index >= i->t->array_size || + !upb_arrhas(int_arrent(i)); + } else { + return i->index >= upb_table_size(&i->t->t) || + upb_tabent_isempty(int_tabent(i)); + } +} + +uintptr_t upb_inttable_iter_key(const upb_inttable_iter *i) { + assert(!upb_inttable_done(i)); + return i->array_part ? i->index : int_tabent(i)->key; +} + +upb_value upb_inttable_iter_value(const upb_inttable_iter *i) { + assert(!upb_inttable_done(i)); + return _upb_value_val( + i->array_part ? i->t->array[i->index].val : int_tabent(i)->val.val, + i->t->t.ctype); +} + +void upb_inttable_iter_setdone(upb_inttable_iter *i) { + i->index = SIZE_MAX; + i->array_part = false; +} + +bool upb_inttable_iter_isequal(const upb_inttable_iter *i1, + const upb_inttable_iter *i2) { + if (upb_inttable_done(i1) && upb_inttable_done(i2)) + return true; + return i1->t == i2->t && i1->index == i2->index && + i1->array_part == i2->array_part; +} + +#ifdef UPB_UNALIGNED_READS_OK +/* ----------------------------------------------------------------------------- + * MurmurHash2, by Austin Appleby (released as public domain). + * Reformatted and C99-ified by Joshua Haberman. + * Note - This code makes a few assumptions about how your machine behaves - + * 1. We can read a 4-byte value from any address without crashing + * 2. sizeof(int) == 4 (in upb this limitation is removed by using uint32_t + * And it has a few limitations - + * 1. It will not work incrementally. + * 2. It will not produce the same results on little-endian and big-endian + * machines. */ +uint32_t MurmurHash2(const void *key, size_t len, uint32_t seed) { + /* 'm' and 'r' are mixing constants generated offline. + * They're not really 'magic', they just happen to work well. */ + const uint32_t m = 0x5bd1e995; + const int32_t r = 24; + + /* Initialize the hash to a 'random' value */ + uint32_t h = seed ^ len; + + /* Mix 4 bytes at a time into the hash */ + const uint8_t * data = (const uint8_t *)key; + while(len >= 4) { + uint32_t k = *(uint32_t *)data; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + /* Handle the last few bytes of the input array */ + switch(len) { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; h *= m; + }; + + /* Do a few final mixes of the hash to ensure the last few + * bytes are well-incorporated. */ + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} + +#else /* !UPB_UNALIGNED_READS_OK */ + +/* ----------------------------------------------------------------------------- + * MurmurHashAligned2, by Austin Appleby + * Same algorithm as MurmurHash2, but only does aligned reads - should be safer + * on certain platforms. + * Performance will be lower than MurmurHash2 */ + +#define MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; } + +uint32_t MurmurHash2(const void * key, size_t len, uint32_t seed) { + const uint32_t m = 0x5bd1e995; + const int32_t r = 24; + const uint8_t * data = (const uint8_t *)key; + uint32_t h = seed ^ len; + uint8_t align = (uintptr_t)data & 3; + + if(align && (len >= 4)) { + /* Pre-load the temp registers */ + uint32_t t = 0, d = 0; + int32_t sl; + int32_t sr; + + switch(align) { + case 1: t |= data[2] << 16; + case 2: t |= data[1] << 8; + case 3: t |= data[0]; + } + + t <<= (8 * align); + + data += 4-align; + len -= 4-align; + + sl = 8 * (4-align); + sr = 8 * align; + + /* Mix */ + + while(len >= 4) { + uint32_t k; + + d = *(uint32_t *)data; + t = (t >> sr) | (d << sl); + + k = t; + + MIX(h,k,m); + + t = d; + + data += 4; + len -= 4; + } + + /* Handle leftover data in temp registers */ + + d = 0; + + if(len >= align) { + uint32_t k; + + switch(align) { + case 3: d |= data[2] << 16; + case 2: d |= data[1] << 8; + case 1: d |= data[0]; + } + + k = (t >> sr) | (d << sl); + MIX(h,k,m); + + data += align; + len -= align; + + /* ---------- + * Handle tail bytes */ + + switch(len) { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; h *= m; + }; + } else { + switch(len) { + case 3: d |= data[2] << 16; + case 2: d |= data[1] << 8; + case 1: d |= data[0]; + case 0: h ^= (t >> sr) | (d << sl); h *= m; + } + } + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; + } else { + while(len >= 4) { + uint32_t k = *(uint32_t *)data; + + MIX(h,k,m); + + data += 4; + len -= 4; + } + + /* ---------- + * Handle tail bytes */ + + switch(len) { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; h *= m; + }; + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; + } +} +#undef MIX + +#endif /* UPB_UNALIGNED_READS_OK */ + +#include +#include +#include +#include +#include +#include +#include + +bool upb_dumptostderr(void *closure, const upb_status* status) { + UPB_UNUSED(closure); + fprintf(stderr, "%s\n", upb_status_errmsg(status)); + return false; +} + +/* Guarantee null-termination and provide ellipsis truncation. + * It may be tempting to "optimize" this by initializing these final + * four bytes up-front and then being careful never to overwrite them, + * this is safer and simpler. */ +static void nullz(upb_status *status) { + const char *ellipsis = "..."; + size_t len = strlen(ellipsis); + assert(sizeof(status->msg) > len); + memcpy(status->msg + sizeof(status->msg) - len, ellipsis, len); +} + +void upb_status_clear(upb_status *status) { + if (!status) return; + status->ok_ = true; + status->code_ = 0; + status->msg[0] = '\0'; +} + +bool upb_ok(const upb_status *status) { return status->ok_; } + +upb_errorspace *upb_status_errspace(const upb_status *status) { + return status->error_space_; +} + +int upb_status_errcode(const upb_status *status) { return status->code_; } + +const char *upb_status_errmsg(const upb_status *status) { return status->msg; } + +void upb_status_seterrmsg(upb_status *status, const char *msg) { + if (!status) return; + status->ok_ = false; + strncpy(status->msg, msg, sizeof(status->msg)); + nullz(status); +} + +void upb_status_seterrf(upb_status *status, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + upb_status_vseterrf(status, fmt, args); + va_end(args); +} + +void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args) { + if (!status) return; + status->ok_ = false; + _upb_vsnprintf(status->msg, sizeof(status->msg), fmt, args); + nullz(status); +} + +void upb_status_seterrcode(upb_status *status, upb_errorspace *space, + int code) { + if (!status) return; + status->ok_ = false; + status->error_space_ = space; + status->code_ = code; + space->set_message(status, code); +} + +void upb_status_copy(upb_status *to, const upb_status *from) { + if (!to) return; + *to = *from; +} +/* This file was generated by upbc (the upb compiler). + * Do not edit -- your changes will be discarded when the file is + * regenerated. */ + + +static const upb_msgdef msgs[20]; +static const upb_fielddef fields[81]; +static const upb_enumdef enums[4]; +static const upb_tabent strentries[236]; +static const upb_tabent intentries[14]; +static const upb_tabval arrays[232]; + +#ifdef UPB_DEBUG_REFS +static upb_inttable reftables[212]; +#endif + +static const upb_msgdef msgs[20] = { + UPB_MSGDEF_INIT("google.protobuf.DescriptorProto", 27, 6, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[0], 8, 7), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[0]),&reftables[0], &reftables[1]), + UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ExtensionRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[8], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[16]),&reftables[2], &reftables[3]), + UPB_MSGDEF_INIT("google.protobuf.EnumDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[11], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[20]),&reftables[4], &reftables[5]), + UPB_MSGDEF_INIT("google.protobuf.EnumOptions", 7, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[0], &arrays[15], 8, 1), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[24]),&reftables[6], &reftables[7]), + UPB_MSGDEF_INIT("google.protobuf.EnumValueDescriptorProto", 8, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[23], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[28]),&reftables[8], &reftables[9]), + UPB_MSGDEF_INIT("google.protobuf.EnumValueOptions", 6, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[2], &arrays[27], 4, 0), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[32]),&reftables[10], &reftables[11]), + UPB_MSGDEF_INIT("google.protobuf.FieldDescriptorProto", 19, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[31], 9, 8), UPB_STRTABLE_INIT(8, 15, UPB_CTYPE_PTR, 4, &strentries[36]),&reftables[12], &reftables[13]), + UPB_MSGDEF_INIT("google.protobuf.FieldOptions", 14, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[4], &arrays[40], 32, 6), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[52]),&reftables[14], &reftables[15]), + UPB_MSGDEF_INIT("google.protobuf.FileDescriptorProto", 39, 6, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[72], 12, 11), UPB_STRTABLE_INIT(11, 15, UPB_CTYPE_PTR, 4, &strentries[68]),&reftables[16], &reftables[17]), + UPB_MSGDEF_INIT("google.protobuf.FileDescriptorSet", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[84], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[84]),&reftables[18], &reftables[19]), + UPB_MSGDEF_INIT("google.protobuf.FileOptions", 21, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[6], &arrays[86], 64, 9), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[88]),&reftables[20], &reftables[21]), + UPB_MSGDEF_INIT("google.protobuf.MessageOptions", 8, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[8], &arrays[150], 16, 2), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[104]),&reftables[22], &reftables[23]), + UPB_MSGDEF_INIT("google.protobuf.MethodDescriptorProto", 13, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[166], 5, 4), UPB_STRTABLE_INIT(4, 7, UPB_CTYPE_PTR, 3, &strentries[108]),&reftables[24], &reftables[25]), + UPB_MSGDEF_INIT("google.protobuf.MethodOptions", 6, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[10], &arrays[171], 4, 0), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[116]),&reftables[26], &reftables[27]), + UPB_MSGDEF_INIT("google.protobuf.ServiceDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[175], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[120]),&reftables[28], &reftables[29]), + UPB_MSGDEF_INIT("google.protobuf.ServiceOptions", 6, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[12], &arrays[179], 4, 0), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[124]),&reftables[30], &reftables[31]), + UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[183], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[128]),&reftables[32], &reftables[33]), + UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo.Location", 14, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[185], 5, 4), UPB_STRTABLE_INIT(4, 7, UPB_CTYPE_PTR, 3, &strentries[132]),&reftables[34], &reftables[35]), + UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption", 18, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[190], 9, 7), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[140]),&reftables[36], &reftables[37]), + UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption.NamePart", 6, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[199], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[156]),&reftables[38], &reftables[39]), +}; + +static const upb_fielddef fields[81] = { + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "aggregate_value", 8, &msgs[18], NULL, 15, 6, {0},&reftables[40], &reftables[41]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "allow_alias", 2, &msgs[3], NULL, 6, 1, {0},&reftables[42], &reftables[43]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "cc_generic_services", 16, &msgs[10], NULL, 17, 6, {0},&reftables[44], &reftables[45]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "ctype", 1, &msgs[7], (const upb_def*)(&enums[2]), 6, 1, {0},&reftables[46], &reftables[47]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "default_value", 7, &msgs[6], NULL, 16, 7, {0},&reftables[48], &reftables[49]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_STRING, 0, false, false, false, false, "dependency", 3, &msgs[8], NULL, 30, 8, {0},&reftables[50], &reftables[51]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 3, &msgs[7], NULL, 8, 3, {0},&reftables[52], &reftables[53]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_DOUBLE, 0, false, false, false, false, "double_value", 6, &msgs[18], NULL, 11, 4, {0},&reftables[54], &reftables[55]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "end", 2, &msgs[1], NULL, 3, 1, {0},&reftables[56], &reftables[57]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 4, &msgs[0], (const upb_def*)(&msgs[2]), 16, 2, {0},&reftables[58], &reftables[59]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 5, &msgs[8], (const upb_def*)(&msgs[2]), 13, 1, {0},&reftables[60], &reftables[61]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "experimental_map_key", 9, &msgs[7], NULL, 10, 5, {0},&reftables[62], &reftables[63]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "extendee", 2, &msgs[6], NULL, 7, 2, {0},&reftables[64], &reftables[65]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension", 7, &msgs[8], (const upb_def*)(&msgs[6]), 19, 3, {0},&reftables[66], &reftables[67]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension", 6, &msgs[0], (const upb_def*)(&msgs[6]), 22, 4, {0},&reftables[68], &reftables[69]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension_range", 5, &msgs[0], (const upb_def*)(&msgs[1]), 19, 3, {0},&reftables[70], &reftables[71]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "field", 2, &msgs[0], (const upb_def*)(&msgs[6]), 10, 0, {0},&reftables[72], &reftables[73]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "file", 1, &msgs[9], (const upb_def*)(&msgs[8]), 5, 0, {0},&reftables[74], &reftables[75]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "go_package", 11, &msgs[10], NULL, 14, 5, {0},&reftables[76], &reftables[77]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "identifier_value", 3, &msgs[18], NULL, 6, 1, {0},&reftables[78], &reftables[79]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "input_type", 2, &msgs[12], NULL, 7, 2, {0},&reftables[80], &reftables[81]), + UPB_FIELDDEF_INIT(UPB_LABEL_REQUIRED, UPB_TYPE_BOOL, 0, false, false, false, false, "is_extension", 2, &msgs[19], NULL, 5, 1, {0},&reftables[82], &reftables[83]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "java_generate_equals_and_hash", 20, &msgs[10], NULL, 20, 9, {0},&reftables[84], &reftables[85]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "java_generic_services", 17, &msgs[10], NULL, 18, 7, {0},&reftables[86], &reftables[87]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "java_multiple_files", 10, &msgs[10], NULL, 13, 4, {0},&reftables[88], &reftables[89]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "java_outer_classname", 8, &msgs[10], NULL, 9, 2, {0},&reftables[90], &reftables[91]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "java_package", 1, &msgs[10], NULL, 6, 1, {0},&reftables[92], &reftables[93]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "label", 4, &msgs[6], (const upb_def*)(&enums[0]), 11, 4, {0},&reftables[94], &reftables[95]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "lazy", 5, &msgs[7], NULL, 9, 4, {0},&reftables[96], &reftables[97]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "leading_comments", 3, &msgs[17], NULL, 8, 2, {0},&reftables[98], &reftables[99]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "location", 1, &msgs[16], (const upb_def*)(&msgs[17]), 5, 0, {0},&reftables[100], &reftables[101]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "message_set_wire_format", 1, &msgs[11], NULL, 6, 1, {0},&reftables[102], &reftables[103]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "message_type", 4, &msgs[8], (const upb_def*)(&msgs[0]), 10, 0, {0},&reftables[104], &reftables[105]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "method", 2, &msgs[14], (const upb_def*)(&msgs[12]), 6, 0, {0},&reftables[106], &reftables[107]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[8], NULL, 22, 6, {0},&reftables[108], &reftables[109]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[14], NULL, 8, 2, {0},&reftables[110], &reftables[111]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "name", 2, &msgs[18], (const upb_def*)(&msgs[19]), 5, 0, {0},&reftables[112], &reftables[113]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[4], NULL, 4, 1, {0},&reftables[114], &reftables[115]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[0], NULL, 24, 6, {0},&reftables[116], &reftables[117]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[12], NULL, 4, 1, {0},&reftables[118], &reftables[119]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[2], NULL, 8, 2, {0},&reftables[120], &reftables[121]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[6], NULL, 4, 1, {0},&reftables[122], &reftables[123]), + UPB_FIELDDEF_INIT(UPB_LABEL_REQUIRED, UPB_TYPE_STRING, 0, false, false, false, false, "name_part", 1, &msgs[19], NULL, 2, 0, {0},&reftables[124], &reftables[125]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT64, UPB_INTFMT_VARIABLE, false, false, false, false, "negative_int_value", 5, &msgs[18], NULL, 10, 3, {0},&reftables[126], &reftables[127]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "nested_type", 3, &msgs[0], (const upb_def*)(&msgs[0]), 13, 1, {0},&reftables[128], &reftables[129]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "no_standard_descriptor_accessor", 2, &msgs[11], NULL, 7, 2, {0},&reftables[130], &reftables[131]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "number", 3, &msgs[6], NULL, 10, 3, {0},&reftables[132], &reftables[133]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "number", 2, &msgs[4], NULL, 7, 2, {0},&reftables[134], &reftables[135]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "optimize_for", 9, &msgs[10], (const upb_def*)(&enums[3]), 12, 3, {0},&reftables[136], &reftables[137]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 7, &msgs[0], (const upb_def*)(&msgs[11]), 23, 5, {0},&reftables[138], &reftables[139]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[2], (const upb_def*)(&msgs[3]), 7, 1, {0},&reftables[140], &reftables[141]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[6], (const upb_def*)(&msgs[7]), 3, 0, {0},&reftables[142], &reftables[143]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[4], (const upb_def*)(&msgs[5]), 3, 0, {0},&reftables[144], &reftables[145]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[8], (const upb_def*)(&msgs[10]), 20, 4, {0},&reftables[146], &reftables[147]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[14], (const upb_def*)(&msgs[15]), 7, 1, {0},&reftables[148], &reftables[149]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 4, &msgs[12], (const upb_def*)(&msgs[13]), 3, 0, {0},&reftables[150], &reftables[151]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "output_type", 3, &msgs[12], NULL, 10, 3, {0},&reftables[152], &reftables[153]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "package", 2, &msgs[8], NULL, 25, 7, {0},&reftables[154], &reftables[155]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "packed", 2, &msgs[7], NULL, 7, 2, {0},&reftables[156], &reftables[157]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, true, "path", 1, &msgs[17], NULL, 4, 0, {0},&reftables[158], &reftables[159]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_UINT64, UPB_INTFMT_VARIABLE, false, false, false, false, "positive_int_value", 4, &msgs[18], NULL, 9, 2, {0},&reftables[160], &reftables[161]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "public_dependency", 10, &msgs[8], NULL, 35, 9, {0},&reftables[162], &reftables[163]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "py_generic_services", 18, &msgs[10], NULL, 19, 8, {0},&reftables[164], &reftables[165]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "service", 6, &msgs[8], (const upb_def*)(&msgs[14]), 16, 2, {0},&reftables[166], &reftables[167]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "source_code_info", 9, &msgs[8], (const upb_def*)(&msgs[16]), 21, 5, {0},&reftables[168], &reftables[169]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, true, "span", 2, &msgs[17], NULL, 7, 1, {0},&reftables[170], &reftables[171]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "start", 1, &msgs[1], NULL, 2, 0, {0},&reftables[172], &reftables[173]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BYTES, 0, false, false, false, false, "string_value", 7, &msgs[18], NULL, 12, 5, {0},&reftables[174], &reftables[175]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "trailing_comments", 4, &msgs[17], NULL, 11, 3, {0},&reftables[176], &reftables[177]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "type", 5, &msgs[6], (const upb_def*)(&enums[1]), 12, 5, {0},&reftables[178], &reftables[179]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "type_name", 6, &msgs[6], NULL, 13, 6, {0},&reftables[180], &reftables[181]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[5], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[182], &reftables[183]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[15], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[184], &reftables[185]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[3], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[186], &reftables[187]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[13], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[188], &reftables[189]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[10], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[190], &reftables[191]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[11], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[192], &reftables[193]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[7], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[194], &reftables[195]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "value", 2, &msgs[2], (const upb_def*)(&msgs[4]), 6, 0, {0},&reftables[196], &reftables[197]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "weak", 10, &msgs[7], NULL, 13, 6, {0},&reftables[198], &reftables[199]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "weak_dependency", 11, &msgs[8], NULL, 38, 10, {0},&reftables[200], &reftables[201]), +}; + +static const upb_enumdef enums[4] = { + UPB_ENUMDEF_INIT("google.protobuf.FieldDescriptorProto.Label", UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_INT32, 2, &strentries[160]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[202], 4, 3), 0, &reftables[202], &reftables[203]), + UPB_ENUMDEF_INIT("google.protobuf.FieldDescriptorProto.Type", UPB_STRTABLE_INIT(18, 31, UPB_CTYPE_INT32, 5, &strentries[164]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[206], 19, 18), 0, &reftables[204], &reftables[205]), + UPB_ENUMDEF_INIT("google.protobuf.FieldOptions.CType", UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_INT32, 2, &strentries[196]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[225], 3, 3), 0, &reftables[206], &reftables[207]), + UPB_ENUMDEF_INIT("google.protobuf.FileOptions.OptimizeMode", UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_INT32, 2, &strentries[200]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[228], 4, 3), 0, &reftables[208], &reftables[209]), +}; + +static const upb_tabent strentries[236] = { + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "extension"), UPB_TABVALUE_PTR_INIT(&fields[14]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[38]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "field"), UPB_TABVALUE_PTR_INIT(&fields[16]), NULL}, + {UPB_TABKEY_STR("\017", "\000", "\000", "\000", "extension_range"), UPB_TABVALUE_PTR_INIT(&fields[15]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "nested_type"), UPB_TABVALUE_PTR_INIT(&fields[44]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[49]), NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "enum_type"), UPB_TABVALUE_PTR_INIT(&fields[9]), &strentries[14]}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "start"), UPB_TABVALUE_PTR_INIT(&fields[66]), NULL}, + {UPB_TABKEY_STR("\003", "\000", "\000", "\000", "end"), UPB_TABVALUE_PTR_INIT(&fields[8]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "value"), UPB_TABVALUE_PTR_INIT(&fields[78]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[50]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[40]), &strentries[22]}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[73]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "allow_alias"), UPB_TABVALUE_PTR_INIT(&fields[1]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "number"), UPB_TABVALUE_PTR_INIT(&fields[47]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[52]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[37]), &strentries[30]}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[71]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "label"), UPB_TABVALUE_PTR_INIT(&fields[27]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[41]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "number"), UPB_TABVALUE_PTR_INIT(&fields[46]), &strentries[49]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "type_name"), UPB_TABVALUE_PTR_INIT(&fields[70]), NULL}, + {UPB_TABKEY_STR("\010", "\000", "\000", "\000", "extendee"), UPB_TABVALUE_PTR_INIT(&fields[12]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "type"), UPB_TABVALUE_PTR_INIT(&fields[69]), &strentries[48]}, + {UPB_TABKEY_STR("\015", "\000", "\000", "\000", "default_value"), UPB_TABVALUE_PTR_INIT(&fields[4]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[51]), NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "experimental_map_key"), UPB_TABVALUE_PTR_INIT(&fields[11]), &strentries[67]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "weak"), UPB_TABVALUE_PTR_INIT(&fields[79]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "packed"), UPB_TABVALUE_PTR_INIT(&fields[58]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "lazy"), UPB_TABVALUE_PTR_INIT(&fields[28]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "ctype"), UPB_TABVALUE_PTR_INIT(&fields[3]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[6]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[77]), NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "extension"), UPB_TABVALUE_PTR_INIT(&fields[13]), NULL}, + {UPB_TABKEY_STR("\017", "\000", "\000", "\000", "weak_dependency"), UPB_TABVALUE_PTR_INIT(&fields[80]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[34]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "service"), UPB_TABVALUE_PTR_INIT(&fields[63]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "source_code_info"), UPB_TABVALUE_PTR_INIT(&fields[64]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "dependency"), UPB_TABVALUE_PTR_INIT(&fields[5]), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "message_type"), UPB_TABVALUE_PTR_INIT(&fields[32]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "package"), UPB_TABVALUE_PTR_INIT(&fields[57]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[53]), &strentries[82]}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "enum_type"), UPB_TABVALUE_PTR_INIT(&fields[10]), NULL}, + {UPB_TABKEY_STR("\021", "\000", "\000", "\000", "public_dependency"), UPB_TABVALUE_PTR_INIT(&fields[61]), &strentries[81]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "file"), UPB_TABVALUE_PTR_INIT(&fields[17]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[75]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\023", "\000", "\000", "\000", "cc_generic_services"), UPB_TABVALUE_PTR_INIT(&fields[2]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\023", "\000", "\000", "\000", "java_multiple_files"), UPB_TABVALUE_PTR_INIT(&fields[24]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\025", "\000", "\000", "\000", "java_generic_services"), UPB_TABVALUE_PTR_INIT(&fields[23]), &strentries[102]}, + {UPB_TABKEY_STR("\035", "\000", "\000", "\000", "java_generate_equals_and_hash"), UPB_TABVALUE_PTR_INIT(&fields[22]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "go_package"), UPB_TABVALUE_PTR_INIT(&fields[18]), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "java_package"), UPB_TABVALUE_PTR_INIT(&fields[26]), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "optimize_for"), UPB_TABVALUE_PTR_INIT(&fields[48]), NULL}, + {UPB_TABKEY_STR("\023", "\000", "\000", "\000", "py_generic_services"), UPB_TABVALUE_PTR_INIT(&fields[62]), NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "java_outer_classname"), UPB_TABVALUE_PTR_INIT(&fields[25]), NULL}, + {UPB_TABKEY_STR("\027", "\000", "\000", "\000", "message_set_wire_format"), UPB_TABVALUE_PTR_INIT(&fields[31]), &strentries[106]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[76]), NULL}, + {UPB_TABKEY_STR("\037", "\000", "\000", "\000", "no_standard_descriptor_accessor"), UPB_TABVALUE_PTR_INIT(&fields[45]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[39]), NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "input_type"), UPB_TABVALUE_PTR_INIT(&fields[20]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "output_type"), UPB_TABVALUE_PTR_INIT(&fields[56]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[55]), NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[74]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[54]), &strentries[122]}, + {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "method"), UPB_TABVALUE_PTR_INIT(&fields[33]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[35]), &strentries[121]}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[72]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\010", "\000", "\000", "\000", "location"), UPB_TABVALUE_PTR_INIT(&fields[30]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "span"), UPB_TABVALUE_PTR_INIT(&fields[65]), &strentries[139]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\021", "\000", "\000", "\000", "trailing_comments"), UPB_TABVALUE_PTR_INIT(&fields[68]), NULL}, + {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "leading_comments"), UPB_TABVALUE_PTR_INIT(&fields[29]), &strentries[137]}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "path"), UPB_TABVALUE_PTR_INIT(&fields[59]), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "double_value"), UPB_TABVALUE_PTR_INIT(&fields[7]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[36]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\022", "\000", "\000", "\000", "negative_int_value"), UPB_TABVALUE_PTR_INIT(&fields[43]), NULL}, + {UPB_TABKEY_STR("\017", "\000", "\000", "\000", "aggregate_value"), UPB_TABVALUE_PTR_INIT(&fields[0]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\022", "\000", "\000", "\000", "positive_int_value"), UPB_TABVALUE_PTR_INIT(&fields[60]), NULL}, + {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "identifier_value"), UPB_TABVALUE_PTR_INIT(&fields[19]), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "string_value"), UPB_TABVALUE_PTR_INIT(&fields[67]), &strentries[154]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "is_extension"), UPB_TABVALUE_PTR_INIT(&fields[21]), NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "name_part"), UPB_TABVALUE_PTR_INIT(&fields[42]), NULL}, + {UPB_TABKEY_STR("\016", "\000", "\000", "\000", "LABEL_REQUIRED"), UPB_TABVALUE_INT_INIT(2), &strentries[162]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\016", "\000", "\000", "\000", "LABEL_REPEATED"), UPB_TABVALUE_INT_INIT(3), NULL}, + {UPB_TABKEY_STR("\016", "\000", "\000", "\000", "LABEL_OPTIONAL"), UPB_TABVALUE_INT_INIT(1), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "TYPE_FIXED64"), UPB_TABVALUE_INT_INIT(6), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_STRING"), UPB_TABVALUE_INT_INIT(9), NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_FLOAT"), UPB_TABVALUE_INT_INIT(2), &strentries[193]}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_DOUBLE"), UPB_TABVALUE_INT_INIT(1), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_INT32"), UPB_TABVALUE_INT_INIT(5), NULL}, + {UPB_TABKEY_STR("\015", "\000", "\000", "\000", "TYPE_SFIXED32"), UPB_TABVALUE_INT_INIT(15), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "TYPE_FIXED32"), UPB_TABVALUE_INT_INIT(7), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "TYPE_MESSAGE"), UPB_TABVALUE_INT_INIT(11), &strentries[194]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_INT64"), UPB_TABVALUE_INT_INIT(3), &strentries[191]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "TYPE_ENUM"), UPB_TABVALUE_INT_INIT(14), NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_UINT32"), UPB_TABVALUE_INT_INIT(13), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_UINT64"), UPB_TABVALUE_INT_INIT(4), &strentries[190]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\015", "\000", "\000", "\000", "TYPE_SFIXED64"), UPB_TABVALUE_INT_INIT(16), NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_BYTES"), UPB_TABVALUE_INT_INIT(12), NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_SINT64"), UPB_TABVALUE_INT_INIT(18), NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "TYPE_BOOL"), UPB_TABVALUE_INT_INIT(8), NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_GROUP"), UPB_TABVALUE_INT_INIT(10), NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_SINT32"), UPB_TABVALUE_INT_INIT(17), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "CORD"), UPB_TABVALUE_INT_INIT(1), NULL}, + {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "STRING"), UPB_TABVALUE_INT_INIT(0), &strentries[197]}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "STRING_PIECE"), UPB_TABVALUE_INT_INIT(2), NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "CODE_SIZE"), UPB_TABVALUE_INT_INIT(2), NULL}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "SPEED"), UPB_TABVALUE_INT_INIT(1), &strentries[203]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "LITE_RUNTIME"), UPB_TABVALUE_INT_INIT(3), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\047", "\000", "\000", "\000", "google.protobuf.SourceCodeInfo.Location"), UPB_TABVALUE_PTR_INIT(&msgs[17]), NULL}, + {UPB_TABKEY_STR("\043", "\000", "\000", "\000", "google.protobuf.UninterpretedOption"), UPB_TABVALUE_PTR_INIT(&msgs[18]), NULL}, + {UPB_TABKEY_STR("\043", "\000", "\000", "\000", "google.protobuf.FileDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[8]), NULL}, + {UPB_TABKEY_STR("\045", "\000", "\000", "\000", "google.protobuf.MethodDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[12]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\040", "\000", "\000", "\000", "google.protobuf.EnumValueOptions"), UPB_TABVALUE_PTR_INIT(&msgs[5]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\037", "\000", "\000", "\000", "google.protobuf.DescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[0]), &strentries[228]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\036", "\000", "\000", "\000", "google.protobuf.SourceCodeInfo"), UPB_TABVALUE_PTR_INIT(&msgs[16]), NULL}, + {UPB_TABKEY_STR("\051", "\000", "\000", "\000", "google.protobuf.FieldDescriptorProto.Type"), UPB_TABVALUE_PTR_INIT(&enums[1]), NULL}, + {UPB_TABKEY_STR("\056", "\000", "\000", "\000", "google.protobuf.DescriptorProto.ExtensionRange"), UPB_TABVALUE_PTR_INIT(&msgs[1]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\050", "\000", "\000", "\000", "google.protobuf.EnumValueDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[4]), NULL}, + {UPB_TABKEY_STR("\034", "\000", "\000", "\000", "google.protobuf.FieldOptions"), UPB_TABVALUE_PTR_INIT(&msgs[7]), NULL}, + {UPB_TABKEY_STR("\033", "\000", "\000", "\000", "google.protobuf.FileOptions"), UPB_TABVALUE_PTR_INIT(&msgs[10]), NULL}, + {UPB_TABKEY_STR("\043", "\000", "\000", "\000", "google.protobuf.EnumDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[2]), &strentries[233]}, + {UPB_TABKEY_STR("\052", "\000", "\000", "\000", "google.protobuf.FieldDescriptorProto.Label"), UPB_TABVALUE_PTR_INIT(&enums[0]), NULL}, + {UPB_TABKEY_STR("\046", "\000", "\000", "\000", "google.protobuf.ServiceDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[14]), NULL}, + {UPB_TABKEY_STR("\042", "\000", "\000", "\000", "google.protobuf.FieldOptions.CType"), UPB_TABVALUE_PTR_INIT(&enums[2]), &strentries[229]}, + {UPB_TABKEY_STR("\041", "\000", "\000", "\000", "google.protobuf.FileDescriptorSet"), UPB_TABVALUE_PTR_INIT(&msgs[9]), &strentries[235]}, + {UPB_TABKEY_STR("\033", "\000", "\000", "\000", "google.protobuf.EnumOptions"), UPB_TABVALUE_PTR_INIT(&msgs[3]), NULL}, + {UPB_TABKEY_STR("\044", "\000", "\000", "\000", "google.protobuf.FieldDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[6]), NULL}, + {UPB_TABKEY_STR("\050", "\000", "\000", "\000", "google.protobuf.FileOptions.OptimizeMode"), UPB_TABVALUE_PTR_INIT(&enums[3]), &strentries[221]}, + {UPB_TABKEY_STR("\036", "\000", "\000", "\000", "google.protobuf.ServiceOptions"), UPB_TABVALUE_PTR_INIT(&msgs[15]), NULL}, + {UPB_TABKEY_STR("\036", "\000", "\000", "\000", "google.protobuf.MessageOptions"), UPB_TABVALUE_PTR_INIT(&msgs[11]), NULL}, + {UPB_TABKEY_STR("\035", "\000", "\000", "\000", "google.protobuf.MethodOptions"), UPB_TABVALUE_PTR_INIT(&msgs[13]), &strentries[226]}, + {UPB_TABKEY_STR("\054", "\000", "\000", "\000", "google.protobuf.UninterpretedOption.NamePart"), UPB_TABVALUE_PTR_INIT(&msgs[19]), NULL}, +}; + +static const upb_tabent intentries[14] = { + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[73]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[71]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[77]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[75]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[76]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[74]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[72]), NULL}, +}; + +static const upb_tabval arrays[232] = { + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[38]), + UPB_TABVALUE_PTR_INIT(&fields[16]), + UPB_TABVALUE_PTR_INIT(&fields[44]), + UPB_TABVALUE_PTR_INIT(&fields[9]), + UPB_TABVALUE_PTR_INIT(&fields[15]), + UPB_TABVALUE_PTR_INIT(&fields[14]), + UPB_TABVALUE_PTR_INIT(&fields[49]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[66]), + UPB_TABVALUE_PTR_INIT(&fields[8]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[40]), + UPB_TABVALUE_PTR_INIT(&fields[78]), + UPB_TABVALUE_PTR_INIT(&fields[50]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[1]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[37]), + UPB_TABVALUE_PTR_INIT(&fields[47]), + UPB_TABVALUE_PTR_INIT(&fields[52]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[41]), + UPB_TABVALUE_PTR_INIT(&fields[12]), + UPB_TABVALUE_PTR_INIT(&fields[46]), + UPB_TABVALUE_PTR_INIT(&fields[27]), + UPB_TABVALUE_PTR_INIT(&fields[69]), + UPB_TABVALUE_PTR_INIT(&fields[70]), + UPB_TABVALUE_PTR_INIT(&fields[4]), + UPB_TABVALUE_PTR_INIT(&fields[51]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[3]), + UPB_TABVALUE_PTR_INIT(&fields[58]), + UPB_TABVALUE_PTR_INIT(&fields[6]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[28]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[11]), + UPB_TABVALUE_PTR_INIT(&fields[79]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[34]), + UPB_TABVALUE_PTR_INIT(&fields[57]), + UPB_TABVALUE_PTR_INIT(&fields[5]), + UPB_TABVALUE_PTR_INIT(&fields[32]), + UPB_TABVALUE_PTR_INIT(&fields[10]), + UPB_TABVALUE_PTR_INIT(&fields[63]), + UPB_TABVALUE_PTR_INIT(&fields[13]), + UPB_TABVALUE_PTR_INIT(&fields[53]), + UPB_TABVALUE_PTR_INIT(&fields[64]), + UPB_TABVALUE_PTR_INIT(&fields[61]), + UPB_TABVALUE_PTR_INIT(&fields[80]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[17]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[26]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[25]), + UPB_TABVALUE_PTR_INIT(&fields[48]), + UPB_TABVALUE_PTR_INIT(&fields[24]), + UPB_TABVALUE_PTR_INIT(&fields[18]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[2]), + UPB_TABVALUE_PTR_INIT(&fields[23]), + UPB_TABVALUE_PTR_INIT(&fields[62]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[22]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[31]), + UPB_TABVALUE_PTR_INIT(&fields[45]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[39]), + UPB_TABVALUE_PTR_INIT(&fields[20]), + UPB_TABVALUE_PTR_INIT(&fields[56]), + UPB_TABVALUE_PTR_INIT(&fields[55]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[35]), + UPB_TABVALUE_PTR_INIT(&fields[33]), + UPB_TABVALUE_PTR_INIT(&fields[54]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[30]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[59]), + UPB_TABVALUE_PTR_INIT(&fields[65]), + UPB_TABVALUE_PTR_INIT(&fields[29]), + UPB_TABVALUE_PTR_INIT(&fields[68]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[36]), + UPB_TABVALUE_PTR_INIT(&fields[19]), + UPB_TABVALUE_PTR_INIT(&fields[60]), + UPB_TABVALUE_PTR_INIT(&fields[43]), + UPB_TABVALUE_PTR_INIT(&fields[7]), + UPB_TABVALUE_PTR_INIT(&fields[67]), + UPB_TABVALUE_PTR_INIT(&fields[0]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[42]), + UPB_TABVALUE_PTR_INIT(&fields[21]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT("LABEL_OPTIONAL"), + UPB_TABVALUE_PTR_INIT("LABEL_REQUIRED"), + UPB_TABVALUE_PTR_INIT("LABEL_REPEATED"), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT("TYPE_DOUBLE"), + UPB_TABVALUE_PTR_INIT("TYPE_FLOAT"), + UPB_TABVALUE_PTR_INIT("TYPE_INT64"), + UPB_TABVALUE_PTR_INIT("TYPE_UINT64"), + UPB_TABVALUE_PTR_INIT("TYPE_INT32"), + UPB_TABVALUE_PTR_INIT("TYPE_FIXED64"), + UPB_TABVALUE_PTR_INIT("TYPE_FIXED32"), + UPB_TABVALUE_PTR_INIT("TYPE_BOOL"), + UPB_TABVALUE_PTR_INIT("TYPE_STRING"), + UPB_TABVALUE_PTR_INIT("TYPE_GROUP"), + UPB_TABVALUE_PTR_INIT("TYPE_MESSAGE"), + UPB_TABVALUE_PTR_INIT("TYPE_BYTES"), + UPB_TABVALUE_PTR_INIT("TYPE_UINT32"), + UPB_TABVALUE_PTR_INIT("TYPE_ENUM"), + UPB_TABVALUE_PTR_INIT("TYPE_SFIXED32"), + UPB_TABVALUE_PTR_INIT("TYPE_SFIXED64"), + UPB_TABVALUE_PTR_INIT("TYPE_SINT32"), + UPB_TABVALUE_PTR_INIT("TYPE_SINT64"), + UPB_TABVALUE_PTR_INIT("STRING"), + UPB_TABVALUE_PTR_INIT("CORD"), + UPB_TABVALUE_PTR_INIT("STRING_PIECE"), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT("SPEED"), + UPB_TABVALUE_PTR_INIT("CODE_SIZE"), + UPB_TABVALUE_PTR_INIT("LITE_RUNTIME"), +}; + +static const upb_symtab symtab = UPB_SYMTAB_INIT(UPB_STRTABLE_INIT(24, 31, UPB_CTYPE_PTR, 5, &strentries[204]), &reftables[210], &reftables[211]); + +const upb_symtab *upbdefs_google_protobuf_descriptor(const void *owner) { + upb_symtab_ref(&symtab, owner); + return &symtab; +} + +#ifdef UPB_DEBUG_REFS +static upb_inttable reftables[212] = { + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), + UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), +}; +#endif + +/* +** XXX: The routines in this file that consume a string do not currently +** support having the string span buffers. In the future, as upb_sink and +** its buffering/sharing functionality evolve there should be an easy and +** idiomatic way of correctly handling this case. For now, we accept this +** limitation since we currently only parse descriptors from single strings. +*/ + + +#include +#include +#include + +/* upb_deflist is an internal-only dynamic array for storing a growing list of + * upb_defs. */ +typedef struct { + upb_def **defs; + size_t len; + size_t size; + bool owned; +} upb_deflist; + +/* We keep a stack of all the messages scopes we are currently in, as well as + * the top-level file scope. This is necessary to correctly qualify the + * definitions that are contained inside. "name" tracks the name of the + * message or package (a bare name -- not qualified by any enclosing scopes). */ +typedef struct { + char *name; + /* Index of the first def that is under this scope. For msgdefs, the + * msgdef itself is at start-1. */ + int start; +} upb_descreader_frame; + +/* The maximum number of nested declarations that are allowed, ie. + * message Foo { + * message Bar { + * message Baz { + * } + * } + * } + * + * This is a resource limit that affects how big our runtime stack can grow. + * TODO: make this a runtime-settable property of the Reader instance. */ +#define UPB_MAX_MESSAGE_NESTING 64 + +struct upb_descreader { + upb_sink sink; + upb_deflist defs; + upb_descreader_frame stack[UPB_MAX_MESSAGE_NESTING]; + int stack_len; + + uint32_t number; + char *name; + bool saw_number; + bool saw_name; + + char *default_string; + + upb_fielddef *f; +}; + +static char *upb_strndup(const char *buf, size_t n) { + char *ret = malloc(n + 1); + if (!ret) return NULL; + memcpy(ret, buf, n); + ret[n] = '\0'; + return ret; +} + +/* Returns a newly allocated string that joins input strings together, for + * example: + * join("Foo.Bar", "Baz") -> "Foo.Bar.Baz" + * join("", "Baz") -> "Baz" + * Caller owns a ref on the returned string. */ +static char *upb_join(const char *base, const char *name) { + if (!base || strlen(base) == 0) { + return upb_strdup(name); + } else { + char *ret = malloc(strlen(base) + strlen(name) + 2); + ret[0] = '\0'; + strcat(ret, base); + strcat(ret, "."); + strcat(ret, name); + return ret; + } +} + + +/* upb_deflist ****************************************************************/ + +void upb_deflist_init(upb_deflist *l) { + l->size = 0; + l->defs = NULL; + l->len = 0; + l->owned = true; +} + +void upb_deflist_uninit(upb_deflist *l) { + size_t i; + if (l->owned) + for(i = 0; i < l->len; i++) + upb_def_unref(l->defs[i], l); + free(l->defs); +} + +bool upb_deflist_push(upb_deflist *l, upb_def *d) { + if(++l->len >= l->size) { + size_t new_size = UPB_MAX(l->size, 4); + new_size *= 2; + l->defs = realloc(l->defs, new_size * sizeof(void *)); + if (!l->defs) return false; + l->size = new_size; + } + l->defs[l->len - 1] = d; + return true; +} + +void upb_deflist_donaterefs(upb_deflist *l, void *owner) { + size_t i; + assert(l->owned); + for (i = 0; i < l->len; i++) + upb_def_donateref(l->defs[i], l, owner); + l->owned = false; +} + +static upb_def *upb_deflist_last(upb_deflist *l) { + return l->defs[l->len-1]; +} + +/* Qualify the defname for all defs starting with offset "start" with "str". */ +static void upb_deflist_qualify(upb_deflist *l, char *str, int32_t start) { + uint32_t i; + for (i = start; i < l->len; i++) { + upb_def *def = l->defs[i]; + char *name = upb_join(str, upb_def_fullname(def)); + upb_def_setfullname(def, name, NULL); + free(name); + } +} + + +/* upb_descreader ************************************************************/ + +static upb_msgdef *upb_descreader_top(upb_descreader *r) { + int index; + assert(r->stack_len > 1); + index = r->stack[r->stack_len-1].start - 1; + assert(index >= 0); + return upb_downcast_msgdef_mutable(r->defs.defs[index]); +} + +static upb_def *upb_descreader_last(upb_descreader *r) { + return upb_deflist_last(&r->defs); +} + +/* Start/end handlers for FileDescriptorProto and DescriptorProto (the two + * entities that have names and can contain sub-definitions. */ +void upb_descreader_startcontainer(upb_descreader *r) { + upb_descreader_frame *f = &r->stack[r->stack_len++]; + f->start = r->defs.len; + f->name = NULL; +} + +void upb_descreader_endcontainer(upb_descreader *r) { + upb_descreader_frame *f = &r->stack[--r->stack_len]; + upb_deflist_qualify(&r->defs, f->name, f->start); + free(f->name); + f->name = NULL; +} + +void upb_descreader_setscopename(upb_descreader *r, char *str) { + upb_descreader_frame *f = &r->stack[r->stack_len-1]; + free(f->name); + f->name = str; +} + +/* Handlers for google.protobuf.FileDescriptorProto. */ +static bool file_startmsg(void *r, const void *hd) { + UPB_UNUSED(hd); + upb_descreader_startcontainer(r); + return true; +} + +static bool file_endmsg(void *closure, const void *hd, upb_status *status) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + UPB_UNUSED(status); + upb_descreader_endcontainer(r); + return true; +} + +static size_t file_onpackage(void *closure, const void *hd, const char *buf, + size_t n, const upb_bufhandle *handle) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + UPB_UNUSED(handle); + /* XXX: see comment at the top of the file. */ + upb_descreader_setscopename(r, upb_strndup(buf, n)); + return n; +} + +/* Handlers for google.protobuf.EnumValueDescriptorProto. */ +static bool enumval_startmsg(void *closure, const void *hd) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + r->saw_number = false; + r->saw_name = false; + return true; +} + +static size_t enumval_onname(void *closure, const void *hd, const char *buf, + size_t n, const upb_bufhandle *handle) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + UPB_UNUSED(handle); + /* XXX: see comment at the top of the file. */ + free(r->name); + r->name = upb_strndup(buf, n); + r->saw_name = true; + return n; +} + +static bool enumval_onnumber(void *closure, const void *hd, int32_t val) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + r->number = val; + r->saw_number = true; + return true; +} + +static bool enumval_endmsg(void *closure, const void *hd, upb_status *status) { + upb_descreader *r = closure; + upb_enumdef *e; + UPB_UNUSED(hd); + + if(!r->saw_number || !r->saw_name) { + upb_status_seterrmsg(status, "Enum value missing name or number."); + return false; + } + e = upb_downcast_enumdef_mutable(upb_descreader_last(r)); + upb_enumdef_addval(e, r->name, r->number, status); + free(r->name); + r->name = NULL; + return true; +} + + +/* Handlers for google.protobuf.EnumDescriptorProto. */ +static bool enum_startmsg(void *closure, const void *hd) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + upb_deflist_push(&r->defs, + upb_enumdef_upcast_mutable(upb_enumdef_new(&r->defs))); + return true; +} + +static bool enum_endmsg(void *closure, const void *hd, upb_status *status) { + upb_descreader *r = closure; + upb_enumdef *e; + UPB_UNUSED(hd); + + e = upb_downcast_enumdef_mutable(upb_descreader_last(r)); + if (upb_def_fullname(upb_descreader_last(r)) == NULL) { + upb_status_seterrmsg(status, "Enum had no name."); + return false; + } + if (upb_enumdef_numvals(e) == 0) { + upb_status_seterrmsg(status, "Enum had no values."); + return false; + } + return true; +} + +static size_t enum_onname(void *closure, const void *hd, const char *buf, + size_t n, const upb_bufhandle *handle) { + upb_descreader *r = closure; + char *fullname = upb_strndup(buf, n); + UPB_UNUSED(hd); + UPB_UNUSED(handle); + /* XXX: see comment at the top of the file. */ + upb_def_setfullname(upb_descreader_last(r), fullname, NULL); + free(fullname); + return n; +} + +/* Handlers for google.protobuf.FieldDescriptorProto */ +static bool field_startmsg(void *closure, const void *hd) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + r->f = upb_fielddef_new(&r->defs); + free(r->default_string); + r->default_string = NULL; + + /* fielddefs default to packed, but descriptors default to non-packed. */ + upb_fielddef_setpacked(r->f, false); + return true; +} + +/* Converts the default value in string "str" into "d". Passes a ref on str. + * Returns true on success. */ +static bool parse_default(char *str, upb_fielddef *f) { + bool success = true; + char *end; + switch (upb_fielddef_type(f)) { + case UPB_TYPE_INT32: { + long val = strtol(str, &end, 0); + if (val > INT32_MAX || val < INT32_MIN || errno == ERANGE || *end) + success = false; + else + upb_fielddef_setdefaultint32(f, val); + break; + } + case UPB_TYPE_INT64: { + /* XXX: Need to write our own strtoll, since it's not available in c89. */ + long long val = strtol(str, &end, 0); + if (val > INT64_MAX || val < INT64_MIN || errno == ERANGE || *end) + success = false; + else + upb_fielddef_setdefaultint64(f, val); + break; + } + case UPB_TYPE_UINT32: { + unsigned long val = strtoul(str, &end, 0); + if (val > UINT32_MAX || errno == ERANGE || *end) + success = false; + else + upb_fielddef_setdefaultuint32(f, val); + break; + } + case UPB_TYPE_UINT64: { + /* XXX: Need to write our own strtoull, since it's not available in c89. */ + unsigned long long val = strtoul(str, &end, 0); + if (val > UINT64_MAX || errno == ERANGE || *end) + success = false; + else + upb_fielddef_setdefaultuint64(f, val); + break; + } + case UPB_TYPE_DOUBLE: { + double val = strtod(str, &end); + if (errno == ERANGE || *end) + success = false; + else + upb_fielddef_setdefaultdouble(f, val); + break; + } + case UPB_TYPE_FLOAT: { + /* XXX: Need to write our own strtof, since it's not available in c89. */ + float val = strtod(str, &end); + if (errno == ERANGE || *end) + success = false; + else + upb_fielddef_setdefaultfloat(f, val); + break; + } + case UPB_TYPE_BOOL: { + if (strcmp(str, "false") == 0) + upb_fielddef_setdefaultbool(f, false); + else if (strcmp(str, "true") == 0) + upb_fielddef_setdefaultbool(f, true); + else + success = false; + break; + } + default: abort(); + } + return success; +} + +static bool field_endmsg(void *closure, const void *hd, upb_status *status) { + upb_descreader *r = closure; + upb_fielddef *f = r->f; + UPB_UNUSED(hd); + + /* TODO: verify that all required fields were present. */ + assert(upb_fielddef_number(f) != 0); + assert(upb_fielddef_name(f) != NULL); + assert((upb_fielddef_subdefname(f) != NULL) == upb_fielddef_hassubdef(f)); + + if (r->default_string) { + if (upb_fielddef_issubmsg(f)) { + upb_status_seterrmsg(status, "Submessages cannot have defaults."); + return false; + } + if (upb_fielddef_isstring(f) || upb_fielddef_type(f) == UPB_TYPE_ENUM) { + upb_fielddef_setdefaultcstr(f, r->default_string, NULL); + } else { + if (r->default_string && !parse_default(r->default_string, f)) { + /* We don't worry too much about giving a great error message since the + * compiler should have ensured this was correct. */ + upb_status_seterrmsg(status, "Error converting default value."); + return false; + } + } + } + return true; +} + +static bool field_onlazy(void *closure, const void *hd, bool val) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + + upb_fielddef_setlazy(r->f, val); + return true; +} + +static bool field_onpacked(void *closure, const void *hd, bool val) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + + upb_fielddef_setpacked(r->f, val); + return true; +} + +static bool field_ontype(void *closure, const void *hd, int32_t val) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + + upb_fielddef_setdescriptortype(r->f, val); + return true; +} + +static bool field_onlabel(void *closure, const void *hd, int32_t val) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + + upb_fielddef_setlabel(r->f, val); + return true; +} + +static bool field_onnumber(void *closure, const void *hd, int32_t val) { + upb_descreader *r = closure; + bool ok = upb_fielddef_setnumber(r->f, val, NULL); + UPB_UNUSED(hd); + + UPB_ASSERT_VAR(ok, ok); + return true; +} + +static size_t field_onname(void *closure, const void *hd, const char *buf, + size_t n, const upb_bufhandle *handle) { + upb_descreader *r = closure; + char *name = upb_strndup(buf, n); + UPB_UNUSED(hd); + UPB_UNUSED(handle); + + /* XXX: see comment at the top of the file. */ + upb_fielddef_setname(r->f, name, NULL); + free(name); + return n; +} + +static size_t field_ontypename(void *closure, const void *hd, const char *buf, + size_t n, const upb_bufhandle *handle) { + upb_descreader *r = closure; + char *name = upb_strndup(buf, n); + UPB_UNUSED(hd); + UPB_UNUSED(handle); + + /* XXX: see comment at the top of the file. */ + upb_fielddef_setsubdefname(r->f, name, NULL); + free(name); + return n; +} + +static size_t field_onextendee(void *closure, const void *hd, const char *buf, + size_t n, const upb_bufhandle *handle) { + upb_descreader *r = closure; + char *name = upb_strndup(buf, n); + UPB_UNUSED(hd); + UPB_UNUSED(handle); + + /* XXX: see comment at the top of the file. */ + upb_fielddef_setcontainingtypename(r->f, name, NULL); + free(name); + return n; +} + +static size_t field_ondefaultval(void *closure, const void *hd, const char *buf, + size_t n, const upb_bufhandle *handle) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + UPB_UNUSED(handle); + + /* Have to convert from string to the correct type, but we might not know the + * type yet, so we save it as a string until the end of the field. + * XXX: see comment at the top of the file. */ + free(r->default_string); + r->default_string = upb_strndup(buf, n); + return n; +} + +/* Handlers for google.protobuf.DescriptorProto (representing a message). */ +static bool msg_startmsg(void *closure, const void *hd) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + + upb_deflist_push(&r->defs, + upb_msgdef_upcast_mutable(upb_msgdef_new(&r->defs))); + upb_descreader_startcontainer(r); + return true; +} + +static bool msg_endmsg(void *closure, const void *hd, upb_status *status) { + upb_descreader *r = closure; + upb_msgdef *m = upb_descreader_top(r); + UPB_UNUSED(hd); + + if(!upb_def_fullname(upb_msgdef_upcast_mutable(m))) { + upb_status_seterrmsg(status, "Encountered message with no name."); + return false; + } + upb_descreader_endcontainer(r); + return true; +} + +static size_t msg_onname(void *closure, const void *hd, const char *buf, + size_t n, const upb_bufhandle *handle) { + upb_descreader *r = closure; + upb_msgdef *m = upb_descreader_top(r); + /* XXX: see comment at the top of the file. */ + char *name = upb_strndup(buf, n); + UPB_UNUSED(hd); + UPB_UNUSED(handle); + + upb_def_setfullname(upb_msgdef_upcast_mutable(m), name, NULL); + upb_descreader_setscopename(r, name); /* Passes ownership of name. */ + return n; +} + +static bool msg_onendfield(void *closure, const void *hd) { + upb_descreader *r = closure; + upb_msgdef *m = upb_descreader_top(r); + UPB_UNUSED(hd); + + upb_msgdef_addfield(m, r->f, &r->defs, NULL); + r->f = NULL; + return true; +} + +static bool pushextension(void *closure, const void *hd) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + + assert(upb_fielddef_containingtypename(r->f)); + upb_fielddef_setisextension(r->f, true); + upb_deflist_push(&r->defs, upb_fielddef_upcast_mutable(r->f)); + r->f = NULL; + return true; +} + +#define D(name) upbdefs_google_protobuf_ ## name(s) + +static void reghandlers(const void *closure, upb_handlers *h) { + const upb_symtab *s = closure; + const upb_msgdef *m = upb_handlers_msgdef(h); + + if (m == D(DescriptorProto)) { + upb_handlers_setstartmsg(h, &msg_startmsg, NULL); + upb_handlers_setendmsg(h, &msg_endmsg, NULL); + upb_handlers_setstring(h, D(DescriptorProto_name), &msg_onname, NULL); + upb_handlers_setendsubmsg(h, D(DescriptorProto_field), &msg_onendfield, + NULL); + upb_handlers_setendsubmsg(h, D(DescriptorProto_extension), &pushextension, + NULL); + } else if (m == D(FileDescriptorProto)) { + upb_handlers_setstartmsg(h, &file_startmsg, NULL); + upb_handlers_setendmsg(h, &file_endmsg, NULL); + upb_handlers_setstring(h, D(FileDescriptorProto_package), &file_onpackage, + NULL); + upb_handlers_setendsubmsg(h, D(FileDescriptorProto_extension), &pushextension, + NULL); + } else if (m == D(EnumValueDescriptorProto)) { + upb_handlers_setstartmsg(h, &enumval_startmsg, NULL); + upb_handlers_setendmsg(h, &enumval_endmsg, NULL); + upb_handlers_setstring(h, D(EnumValueDescriptorProto_name), &enumval_onname, NULL); + upb_handlers_setint32(h, D(EnumValueDescriptorProto_number), &enumval_onnumber, + NULL); + } else if (m == D(EnumDescriptorProto)) { + upb_handlers_setstartmsg(h, &enum_startmsg, NULL); + upb_handlers_setendmsg(h, &enum_endmsg, NULL); + upb_handlers_setstring(h, D(EnumDescriptorProto_name), &enum_onname, NULL); + } else if (m == D(FieldDescriptorProto)) { + upb_handlers_setstartmsg(h, &field_startmsg, NULL); + upb_handlers_setendmsg(h, &field_endmsg, NULL); + upb_handlers_setint32(h, D(FieldDescriptorProto_type), &field_ontype, + NULL); + upb_handlers_setint32(h, D(FieldDescriptorProto_label), &field_onlabel, + NULL); + upb_handlers_setint32(h, D(FieldDescriptorProto_number), &field_onnumber, + NULL); + upb_handlers_setstring(h, D(FieldDescriptorProto_name), &field_onname, + NULL); + upb_handlers_setstring(h, D(FieldDescriptorProto_type_name), + &field_ontypename, NULL); + upb_handlers_setstring(h, D(FieldDescriptorProto_extendee), + &field_onextendee, NULL); + upb_handlers_setstring(h, D(FieldDescriptorProto_default_value), + &field_ondefaultval, NULL); + } else if (m == D(FieldOptions)) { + upb_handlers_setbool(h, D(FieldOptions_lazy), &field_onlazy, NULL); + upb_handlers_setbool(h, D(FieldOptions_packed), &field_onpacked, NULL); + } +} + +#undef D + +void descreader_cleanup(void *_r) { + upb_descreader *r = _r; + free(r->name); + upb_deflist_uninit(&r->defs); + free(r->default_string); + while (r->stack_len > 0) { + upb_descreader_frame *f = &r->stack[--r->stack_len]; + free(f->name); + } +} + + +/* Public API ****************************************************************/ + +upb_descreader *upb_descreader_create(upb_env *e, const upb_handlers *h) { + upb_descreader *r = upb_env_malloc(e, sizeof(upb_descreader)); + if (!r || !upb_env_addcleanup(e, descreader_cleanup, r)) { + return NULL; + } + + upb_deflist_init(&r->defs); + upb_sink_reset(upb_descreader_input(r), h, r); + r->stack_len = 0; + r->name = NULL; + r->default_string = NULL; + + return r; +} + +upb_def **upb_descreader_getdefs(upb_descreader *r, void *owner, int *n) { + *n = r->defs.len; + upb_deflist_donaterefs(&r->defs, owner); + return r->defs.defs; +} + +upb_sink *upb_descreader_input(upb_descreader *r) { + return &r->sink; +} + +const upb_handlers *upb_descreader_newhandlers(const void *owner) { + const upb_symtab *s = upbdefs_google_protobuf_descriptor(&s); + const upb_handlers *h = upb_handlers_newfrozen( + upbdefs_google_protobuf_FileDescriptorSet(s), owner, reghandlers, s); + upb_symtab_unref(s, &s); + return h; +} +/* +** protobuf decoder bytecode compiler +** +** Code to compile a upb::Handlers into bytecode for decoding a protobuf +** according to that specific schema and destination handlers. +** +** Compiling to bytecode is always the first step. If we are using the +** interpreted decoder we leave it as bytecode and interpret that. If we are +** using a JIT decoder we use a code generator to turn the bytecode into native +** code, LLVM IR, etc. +** +** Bytecode definition is in decoder.int.h. +*/ + +#include + +#ifdef UPB_DUMP_BYTECODE +#include +#endif + +#define MAXLABEL 5 +#define EMPTYLABEL -1 + +/* mgroup *********************************************************************/ + +static void freegroup(upb_refcounted *r) { + mgroup *g = (mgroup*)r; + upb_inttable_uninit(&g->methods); +#ifdef UPB_USE_JIT_X64 + upb_pbdecoder_freejit(g); +#endif + free(g->bytecode); + free(g); +} + +static void visitgroup(const upb_refcounted *r, upb_refcounted_visit *visit, + void *closure) { + const mgroup *g = (const mgroup*)r; + upb_inttable_iter i; + upb_inttable_begin(&i, &g->methods); + for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { + upb_pbdecodermethod *method = upb_value_getptr(upb_inttable_iter_value(&i)); + visit(r, upb_pbdecodermethod_upcast(method), closure); + } +} + +mgroup *newgroup(const void *owner) { + mgroup *g = malloc(sizeof(*g)); + static const struct upb_refcounted_vtbl vtbl = {visitgroup, freegroup}; + upb_refcounted_init(mgroup_upcast_mutable(g), &vtbl, owner); + upb_inttable_init(&g->methods, UPB_CTYPE_PTR); + g->bytecode = NULL; + g->bytecode_end = NULL; + return g; +} + + +/* upb_pbdecodermethod ********************************************************/ + +static void freemethod(upb_refcounted *r) { + upb_pbdecodermethod *method = (upb_pbdecodermethod*)r; + + if (method->dest_handlers_) { + upb_handlers_unref(method->dest_handlers_, method); + } + + upb_inttable_uninit(&method->dispatch); + free(method); +} + +static void visitmethod(const upb_refcounted *r, upb_refcounted_visit *visit, + void *closure) { + const upb_pbdecodermethod *m = (const upb_pbdecodermethod*)r; + visit(r, m->group, closure); +} + +static upb_pbdecodermethod *newmethod(const upb_handlers *dest_handlers, + mgroup *group) { + static const struct upb_refcounted_vtbl vtbl = {visitmethod, freemethod}; + upb_pbdecodermethod *ret = malloc(sizeof(*ret)); + upb_refcounted_init(upb_pbdecodermethod_upcast_mutable(ret), &vtbl, &ret); + upb_byteshandler_init(&ret->input_handler_); + + /* The method references the group and vice-versa, in a circular reference. */ + upb_ref2(ret, group); + upb_ref2(group, ret); + upb_inttable_insertptr(&group->methods, dest_handlers, upb_value_ptr(ret)); + upb_pbdecodermethod_unref(ret, &ret); + + ret->group = mgroup_upcast_mutable(group); + ret->dest_handlers_ = dest_handlers; + ret->is_native_ = false; /* If we JIT, it will update this later. */ + upb_inttable_init(&ret->dispatch, UPB_CTYPE_UINT64); + + if (ret->dest_handlers_) { + upb_handlers_ref(ret->dest_handlers_, ret); + } + return ret; +} + +const upb_handlers *upb_pbdecodermethod_desthandlers( + const upb_pbdecodermethod *m) { + return m->dest_handlers_; +} + +const upb_byteshandler *upb_pbdecodermethod_inputhandler( + const upb_pbdecodermethod *m) { + return &m->input_handler_; +} + +bool upb_pbdecodermethod_isnative(const upb_pbdecodermethod *m) { + return m->is_native_; +} + +const upb_pbdecodermethod *upb_pbdecodermethod_new( + const upb_pbdecodermethodopts *opts, const void *owner) { + const upb_pbdecodermethod *ret; + upb_pbcodecache cache; + + upb_pbcodecache_init(&cache); + ret = upb_pbcodecache_getdecodermethod(&cache, opts); + upb_pbdecodermethod_ref(ret, owner); + upb_pbcodecache_uninit(&cache); + return ret; +} + + +/* bytecode compiler **********************************************************/ + +/* Data used only at compilation time. */ +typedef struct { + mgroup *group; + + uint32_t *pc; + int fwd_labels[MAXLABEL]; + int back_labels[MAXLABEL]; + + /* For fields marked "lazy", parse them lazily or eagerly? */ + bool lazy; +} compiler; + +static compiler *newcompiler(mgroup *group, bool lazy) { + compiler *ret = malloc(sizeof(*ret)); + int i; + + ret->group = group; + ret->lazy = lazy; + for (i = 0; i < MAXLABEL; i++) { + ret->fwd_labels[i] = EMPTYLABEL; + ret->back_labels[i] = EMPTYLABEL; + } + return ret; +} + +static void freecompiler(compiler *c) { + free(c); +} + +const size_t ptr_words = sizeof(void*) / sizeof(uint32_t); + +/* How many words an instruction is. */ +static int instruction_len(uint32_t instr) { + switch (getop(instr)) { + case OP_SETDISPATCH: return 1 + ptr_words; + case OP_TAGN: return 3; + case OP_SETBIGGROUPNUM: return 2; + default: return 1; + } +} + +bool op_has_longofs(int32_t instruction) { + switch (getop(instruction)) { + case OP_CALL: + case OP_BRANCH: + case OP_CHECKDELIM: + return true; + /* The "tag" instructions only have 8 bytes available for the jump target, + * but that is ok because these opcodes only require short jumps. */ + case OP_TAG1: + case OP_TAG2: + case OP_TAGN: + return false; + default: + assert(false); + return false; + } +} + +static int32_t getofs(uint32_t instruction) { + if (op_has_longofs(instruction)) { + return (int32_t)instruction >> 8; + } else { + return (int8_t)(instruction >> 8); + } +} + +static void setofs(uint32_t *instruction, int32_t ofs) { + if (op_has_longofs(*instruction)) { + *instruction = getop(*instruction) | ofs << 8; + } else { + *instruction = (*instruction & ~0xff00) | ((ofs & 0xff) << 8); + } + assert(getofs(*instruction) == ofs); /* Would fail in cases of overflow. */ +} + +static uint32_t pcofs(compiler *c) { return c->pc - c->group->bytecode; } + +/* Defines a local label at the current PC location. All previous forward + * references are updated to point to this location. The location is noted + * for any future backward references. */ +static void label(compiler *c, unsigned int label) { + int val; + uint32_t *codep; + + assert(label < MAXLABEL); + val = c->fwd_labels[label]; + codep = (val == EMPTYLABEL) ? NULL : c->group->bytecode + val; + while (codep) { + int ofs = getofs(*codep); + setofs(codep, c->pc - codep - instruction_len(*codep)); + codep = ofs ? codep + ofs : NULL; + } + c->fwd_labels[label] = EMPTYLABEL; + c->back_labels[label] = pcofs(c); +} + +/* Creates a reference to a numbered label; either a forward reference + * (positive arg) or backward reference (negative arg). For forward references + * the value returned now is actually a "next" pointer into a linked list of all + * instructions that use this label and will be patched later when the label is + * defined with label(). + * + * The returned value is the offset that should be written into the instruction. + */ +static int32_t labelref(compiler *c, int label) { + assert(label < MAXLABEL); + if (label == LABEL_DISPATCH) { + /* No resolving required. */ + return 0; + } else if (label < 0) { + /* Backward local label. Relative to the next instruction. */ + uint32_t from = (c->pc + 1) - c->group->bytecode; + return c->back_labels[-label] - from; + } else { + /* Forward local label: prepend to (possibly-empty) linked list. */ + int *lptr = &c->fwd_labels[label]; + int32_t ret = (*lptr == EMPTYLABEL) ? 0 : *lptr - pcofs(c); + *lptr = pcofs(c); + return ret; + } +} + +static void put32(compiler *c, uint32_t v) { + mgroup *g = c->group; + if (c->pc == g->bytecode_end) { + int ofs = pcofs(c); + size_t oldsize = g->bytecode_end - g->bytecode; + size_t newsize = UPB_MAX(oldsize * 2, 64); + /* TODO(haberman): handle OOM. */ + g->bytecode = realloc(g->bytecode, newsize * sizeof(uint32_t)); + g->bytecode_end = g->bytecode + newsize; + c->pc = g->bytecode + ofs; + } + *c->pc++ = v; +} + +static void putop(compiler *c, opcode op, ...) { + va_list ap; + va_start(ap, op); + + switch (op) { + case OP_SETDISPATCH: { + uintptr_t ptr = (uintptr_t)va_arg(ap, void*); + put32(c, OP_SETDISPATCH); + put32(c, ptr); + if (sizeof(uintptr_t) > sizeof(uint32_t)) + put32(c, (uint64_t)ptr >> 32); + break; + } + case OP_STARTMSG: + case OP_ENDMSG: + case OP_PUSHLENDELIM: + case OP_POP: + case OP_SETDELIM: + case OP_HALT: + case OP_RET: + case OP_DISPATCH: + put32(c, op); + break; + case OP_PARSE_DOUBLE: + case OP_PARSE_FLOAT: + case OP_PARSE_INT64: + case OP_PARSE_UINT64: + case OP_PARSE_INT32: + case OP_PARSE_FIXED64: + case OP_PARSE_FIXED32: + case OP_PARSE_BOOL: + case OP_PARSE_UINT32: + case OP_PARSE_SFIXED32: + case OP_PARSE_SFIXED64: + case OP_PARSE_SINT32: + case OP_PARSE_SINT64: + case OP_STARTSEQ: + case OP_ENDSEQ: + case OP_STARTSUBMSG: + case OP_ENDSUBMSG: + case OP_STARTSTR: + case OP_STRING: + case OP_ENDSTR: + case OP_PUSHTAGDELIM: + put32(c, op | va_arg(ap, upb_selector_t) << 8); + break; + case OP_SETBIGGROUPNUM: + put32(c, op); + put32(c, va_arg(ap, int)); + break; + case OP_CALL: { + const upb_pbdecodermethod *method = va_arg(ap, upb_pbdecodermethod *); + put32(c, op | (method->code_base.ofs - (pcofs(c) + 1)) << 8); + break; + } + case OP_CHECKDELIM: + case OP_BRANCH: { + uint32_t instruction = op; + int label = va_arg(ap, int); + setofs(&instruction, labelref(c, label)); + put32(c, instruction); + break; + } + case OP_TAG1: + case OP_TAG2: { + int label = va_arg(ap, int); + uint64_t tag = va_arg(ap, uint64_t); + uint32_t instruction = op | (tag << 16); + assert(tag <= 0xffff); + setofs(&instruction, labelref(c, label)); + put32(c, instruction); + break; + } + case OP_TAGN: { + int label = va_arg(ap, int); + uint64_t tag = va_arg(ap, uint64_t); + uint32_t instruction = op | (upb_value_size(tag) << 16); + setofs(&instruction, labelref(c, label)); + put32(c, instruction); + put32(c, tag); + put32(c, tag >> 32); + break; + } + } + + va_end(ap); +} + +#if defined(UPB_USE_JIT_X64) || defined(UPB_DUMP_BYTECODE) + +const char *upb_pbdecoder_getopname(unsigned int op) { +#define QUOTE(x) #x +#define EXPAND_AND_QUOTE(x) QUOTE(x) +#define OPNAME(x) OP_##x +#define OP(x) case OPNAME(x): return EXPAND_AND_QUOTE(OPNAME(x)); +#define T(x) OP(PARSE_##x) + /* Keep in sync with list in decoder.int.h. */ + switch ((opcode)op) { + T(DOUBLE) T(FLOAT) T(INT64) T(UINT64) T(INT32) T(FIXED64) T(FIXED32) + T(BOOL) T(UINT32) T(SFIXED32) T(SFIXED64) T(SINT32) T(SINT64) + OP(STARTMSG) OP(ENDMSG) OP(STARTSEQ) OP(ENDSEQ) OP(STARTSUBMSG) + OP(ENDSUBMSG) OP(STARTSTR) OP(STRING) OP(ENDSTR) OP(CALL) OP(RET) + OP(PUSHLENDELIM) OP(PUSHTAGDELIM) OP(SETDELIM) OP(CHECKDELIM) + OP(BRANCH) OP(TAG1) OP(TAG2) OP(TAGN) OP(SETDISPATCH) OP(POP) + OP(SETBIGGROUPNUM) OP(DISPATCH) OP(HALT) + } + return ""; +#undef OP +#undef T +} + +#endif + +#ifdef UPB_DUMP_BYTECODE + +static void dumpbc(uint32_t *p, uint32_t *end, FILE *f) { + + uint32_t *begin = p; + + while (p < end) { + fprintf(f, "%p %8tx", p, p - begin); + uint32_t instr = *p++; + uint8_t op = getop(instr); + fprintf(f, " %s", upb_pbdecoder_getopname(op)); + switch ((opcode)op) { + case OP_SETDISPATCH: { + const upb_inttable *dispatch; + memcpy(&dispatch, p, sizeof(void*)); + p += ptr_words; + const upb_pbdecodermethod *method = + (void *)((char *)dispatch - + offsetof(upb_pbdecodermethod, dispatch)); + fprintf(f, " %s", upb_msgdef_fullname( + upb_handlers_msgdef(method->dest_handlers_))); + break; + } + case OP_DISPATCH: + case OP_STARTMSG: + case OP_ENDMSG: + case OP_PUSHLENDELIM: + case OP_POP: + case OP_SETDELIM: + case OP_HALT: + case OP_RET: + break; + case OP_PARSE_DOUBLE: + case OP_PARSE_FLOAT: + case OP_PARSE_INT64: + case OP_PARSE_UINT64: + case OP_PARSE_INT32: + case OP_PARSE_FIXED64: + case OP_PARSE_FIXED32: + case OP_PARSE_BOOL: + case OP_PARSE_UINT32: + case OP_PARSE_SFIXED32: + case OP_PARSE_SFIXED64: + case OP_PARSE_SINT32: + case OP_PARSE_SINT64: + case OP_STARTSEQ: + case OP_ENDSEQ: + case OP_STARTSUBMSG: + case OP_ENDSUBMSG: + case OP_STARTSTR: + case OP_STRING: + case OP_ENDSTR: + case OP_PUSHTAGDELIM: + fprintf(f, " %d", instr >> 8); + break; + case OP_SETBIGGROUPNUM: + fprintf(f, " %d", *p++); + break; + case OP_CHECKDELIM: + case OP_CALL: + case OP_BRANCH: + fprintf(f, " =>0x%tx", p + getofs(instr) - begin); + break; + case OP_TAG1: + case OP_TAG2: { + fprintf(f, " tag:0x%x", instr >> 16); + if (getofs(instr)) { + fprintf(f, " =>0x%tx", p + getofs(instr) - begin); + } + break; + } + case OP_TAGN: { + uint64_t tag = *p++; + tag |= (uint64_t)*p++ << 32; + fprintf(f, " tag:0x%llx", (long long)tag); + fprintf(f, " n:%d", instr >> 16); + if (getofs(instr)) { + fprintf(f, " =>0x%tx", p + getofs(instr) - begin); + } + break; + } + } + fputs("\n", f); + } +} + +#endif + +static uint64_t get_encoded_tag(const upb_fielddef *f, int wire_type) { + uint32_t tag = (upb_fielddef_number(f) << 3) | wire_type; + uint64_t encoded_tag = upb_vencode32(tag); + /* No tag should be greater than 5 bytes. */ + assert(encoded_tag <= 0xffffffffff); + return encoded_tag; +} + +static void putchecktag(compiler *c, const upb_fielddef *f, + int wire_type, int dest) { + uint64_t tag = get_encoded_tag(f, wire_type); + switch (upb_value_size(tag)) { + case 1: + putop(c, OP_TAG1, dest, tag); + break; + case 2: + putop(c, OP_TAG2, dest, tag); + break; + default: + putop(c, OP_TAGN, dest, tag); + break; + } +} + +static upb_selector_t getsel(const upb_fielddef *f, upb_handlertype_t type) { + upb_selector_t selector; + bool ok = upb_handlers_getselector(f, type, &selector); + UPB_ASSERT_VAR(ok, ok); + return selector; +} + +/* Takes an existing, primary dispatch table entry and repacks it with a + * different alternate wire type. Called when we are inserting a secondary + * dispatch table entry for an alternate wire type. */ +static uint64_t repack(uint64_t dispatch, int new_wt2) { + uint64_t ofs; + uint8_t wt1; + uint8_t old_wt2; + upb_pbdecoder_unpackdispatch(dispatch, &ofs, &wt1, &old_wt2); + assert(old_wt2 == NO_WIRE_TYPE); /* wt2 should not be set yet. */ + return upb_pbdecoder_packdispatch(ofs, wt1, new_wt2); +} + +/* Marks the current bytecode position as the dispatch target for this message, + * field, and wire type. */ +static void dispatchtarget(compiler *c, upb_pbdecodermethod *method, + const upb_fielddef *f, int wire_type) { + /* Offset is relative to msg base. */ + uint64_t ofs = pcofs(c) - method->code_base.ofs; + uint32_t fn = upb_fielddef_number(f); + upb_inttable *d = &method->dispatch; + upb_value v; + if (upb_inttable_remove(d, fn, &v)) { + /* TODO: prioritize based on packed setting in .proto file. */ + uint64_t repacked = repack(upb_value_getuint64(v), wire_type); + upb_inttable_insert(d, fn, upb_value_uint64(repacked)); + upb_inttable_insert(d, fn + UPB_MAX_FIELDNUMBER, upb_value_uint64(ofs)); + } else { + uint64_t val = upb_pbdecoder_packdispatch(ofs, wire_type, NO_WIRE_TYPE); + upb_inttable_insert(d, fn, upb_value_uint64(val)); + } +} + +static void putpush(compiler *c, const upb_fielddef *f) { + if (upb_fielddef_descriptortype(f) == UPB_DESCRIPTOR_TYPE_MESSAGE) { + putop(c, OP_PUSHLENDELIM); + } else { + uint32_t fn = upb_fielddef_number(f); + if (fn >= 1 << 24) { + putop(c, OP_PUSHTAGDELIM, 0); + putop(c, OP_SETBIGGROUPNUM, fn); + } else { + putop(c, OP_PUSHTAGDELIM, fn); + } + } +} + +static upb_pbdecodermethod *find_submethod(const compiler *c, + const upb_pbdecodermethod *method, + const upb_fielddef *f) { + const upb_handlers *sub = + upb_handlers_getsubhandlers(method->dest_handlers_, f); + upb_value v; + return upb_inttable_lookupptr(&c->group->methods, sub, &v) + ? upb_value_getptr(v) + : NULL; +} + +static void putsel(compiler *c, opcode op, upb_selector_t sel, + const upb_handlers *h) { + if (upb_handlers_gethandler(h, sel)) { + putop(c, op, sel); + } +} + +/* Puts an opcode to call a callback, but only if a callback actually exists for + * this field and handler type. */ +static void maybeput(compiler *c, opcode op, const upb_handlers *h, + const upb_fielddef *f, upb_handlertype_t type) { + putsel(c, op, getsel(f, type), h); +} + +static bool haslazyhandlers(const upb_handlers *h, const upb_fielddef *f) { + if (!upb_fielddef_lazy(f)) + return false; + + return upb_handlers_gethandler(h, getsel(f, UPB_HANDLER_STARTSTR)) || + upb_handlers_gethandler(h, getsel(f, UPB_HANDLER_STRING)) || + upb_handlers_gethandler(h, getsel(f, UPB_HANDLER_ENDSTR)); +} + + +/* bytecode compiler code generation ******************************************/ + +/* Symbolic names for our local labels. */ +#define LABEL_LOOPSTART 1 /* Top of a repeated field loop. */ +#define LABEL_LOOPBREAK 2 /* To jump out of a repeated loop */ +#define LABEL_FIELD 3 /* Jump backward to find the most recent field. */ +#define LABEL_ENDMSG 4 /* To reach the OP_ENDMSG instr for this msg. */ + +/* Generates bytecode to parse a single non-lazy message field. */ +static void generate_msgfield(compiler *c, const upb_fielddef *f, + upb_pbdecodermethod *method) { + const upb_handlers *h = upb_pbdecodermethod_desthandlers(method); + const upb_pbdecodermethod *sub_m = find_submethod(c, method, f); + int wire_type; + + if (!sub_m) { + /* Don't emit any code for this field at all; it will be parsed as an + * unknown field. */ + return; + } + + label(c, LABEL_FIELD); + + wire_type = + (upb_fielddef_descriptortype(f) == UPB_DESCRIPTOR_TYPE_MESSAGE) + ? UPB_WIRE_TYPE_DELIMITED + : UPB_WIRE_TYPE_START_GROUP; + + if (upb_fielddef_isseq(f)) { + putop(c, OP_CHECKDELIM, LABEL_ENDMSG); + putchecktag(c, f, wire_type, LABEL_DISPATCH); + dispatchtarget(c, method, f, wire_type); + putop(c, OP_PUSHTAGDELIM, 0); + putop(c, OP_STARTSEQ, getsel(f, UPB_HANDLER_STARTSEQ)); + label(c, LABEL_LOOPSTART); + putpush(c, f); + putop(c, OP_STARTSUBMSG, getsel(f, UPB_HANDLER_STARTSUBMSG)); + putop(c, OP_CALL, sub_m); + putop(c, OP_POP); + maybeput(c, OP_ENDSUBMSG, h, f, UPB_HANDLER_ENDSUBMSG); + if (wire_type == UPB_WIRE_TYPE_DELIMITED) { + putop(c, OP_SETDELIM); + } + putop(c, OP_CHECKDELIM, LABEL_LOOPBREAK); + putchecktag(c, f, wire_type, LABEL_LOOPBREAK); + putop(c, OP_BRANCH, -LABEL_LOOPSTART); + label(c, LABEL_LOOPBREAK); + putop(c, OP_POP); + maybeput(c, OP_ENDSEQ, h, f, UPB_HANDLER_ENDSEQ); + } else { + putop(c, OP_CHECKDELIM, LABEL_ENDMSG); + putchecktag(c, f, wire_type, LABEL_DISPATCH); + dispatchtarget(c, method, f, wire_type); + putpush(c, f); + putop(c, OP_STARTSUBMSG, getsel(f, UPB_HANDLER_STARTSUBMSG)); + putop(c, OP_CALL, sub_m); + putop(c, OP_POP); + maybeput(c, OP_ENDSUBMSG, h, f, UPB_HANDLER_ENDSUBMSG); + if (wire_type == UPB_WIRE_TYPE_DELIMITED) { + putop(c, OP_SETDELIM); + } + } +} + +/* Generates bytecode to parse a single string or lazy submessage field. */ +static void generate_delimfield(compiler *c, const upb_fielddef *f, + upb_pbdecodermethod *method) { + const upb_handlers *h = upb_pbdecodermethod_desthandlers(method); + + label(c, LABEL_FIELD); + if (upb_fielddef_isseq(f)) { + putop(c, OP_CHECKDELIM, LABEL_ENDMSG); + putchecktag(c, f, UPB_WIRE_TYPE_DELIMITED, LABEL_DISPATCH); + dispatchtarget(c, method, f, UPB_WIRE_TYPE_DELIMITED); + putop(c, OP_PUSHTAGDELIM, 0); + putop(c, OP_STARTSEQ, getsel(f, UPB_HANDLER_STARTSEQ)); + label(c, LABEL_LOOPSTART); + putop(c, OP_PUSHLENDELIM); + putop(c, OP_STARTSTR, getsel(f, UPB_HANDLER_STARTSTR)); + /* Need to emit even if no handler to skip past the string. */ + putop(c, OP_STRING, getsel(f, UPB_HANDLER_STRING)); + putop(c, OP_POP); + maybeput(c, OP_ENDSTR, h, f, UPB_HANDLER_ENDSTR); + putop(c, OP_SETDELIM); + putop(c, OP_CHECKDELIM, LABEL_LOOPBREAK); + putchecktag(c, f, UPB_WIRE_TYPE_DELIMITED, LABEL_LOOPBREAK); + putop(c, OP_BRANCH, -LABEL_LOOPSTART); + label(c, LABEL_LOOPBREAK); + putop(c, OP_POP); + maybeput(c, OP_ENDSEQ, h, f, UPB_HANDLER_ENDSEQ); + } else { + putop(c, OP_CHECKDELIM, LABEL_ENDMSG); + putchecktag(c, f, UPB_WIRE_TYPE_DELIMITED, LABEL_DISPATCH); + dispatchtarget(c, method, f, UPB_WIRE_TYPE_DELIMITED); + putop(c, OP_PUSHLENDELIM); + putop(c, OP_STARTSTR, getsel(f, UPB_HANDLER_STARTSTR)); + putop(c, OP_STRING, getsel(f, UPB_HANDLER_STRING)); + putop(c, OP_POP); + maybeput(c, OP_ENDSTR, h, f, UPB_HANDLER_ENDSTR); + putop(c, OP_SETDELIM); + } +} + +/* Generates bytecode to parse a single primitive field. */ +static void generate_primitivefield(compiler *c, const upb_fielddef *f, + upb_pbdecodermethod *method) { + const upb_handlers *h = upb_pbdecodermethod_desthandlers(method); + upb_descriptortype_t descriptor_type = upb_fielddef_descriptortype(f); + opcode parse_type; + upb_selector_t sel; + int wire_type; + + label(c, LABEL_FIELD); + + /* From a decoding perspective, ENUM is the same as INT32. */ + if (descriptor_type == UPB_DESCRIPTOR_TYPE_ENUM) + descriptor_type = UPB_DESCRIPTOR_TYPE_INT32; + + parse_type = (opcode)descriptor_type; + + /* TODO(haberman): generate packed or non-packed first depending on "packed" + * setting in the fielddef. This will favor (in speed) whichever was + * specified. */ + + assert((int)parse_type >= 0 && parse_type <= OP_MAX); + sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); + wire_type = upb_pb_native_wire_types[upb_fielddef_descriptortype(f)]; + if (upb_fielddef_isseq(f)) { + putop(c, OP_CHECKDELIM, LABEL_ENDMSG); + putchecktag(c, f, UPB_WIRE_TYPE_DELIMITED, LABEL_DISPATCH); + dispatchtarget(c, method, f, UPB_WIRE_TYPE_DELIMITED); + putop(c, OP_PUSHLENDELIM); + putop(c, OP_STARTSEQ, getsel(f, UPB_HANDLER_STARTSEQ)); /* Packed */ + label(c, LABEL_LOOPSTART); + putop(c, parse_type, sel); + putop(c, OP_CHECKDELIM, LABEL_LOOPBREAK); + putop(c, OP_BRANCH, -LABEL_LOOPSTART); + dispatchtarget(c, method, f, wire_type); + putop(c, OP_PUSHTAGDELIM, 0); + putop(c, OP_STARTSEQ, getsel(f, UPB_HANDLER_STARTSEQ)); /* Non-packed */ + label(c, LABEL_LOOPSTART); + putop(c, parse_type, sel); + putop(c, OP_CHECKDELIM, LABEL_LOOPBREAK); + putchecktag(c, f, wire_type, LABEL_LOOPBREAK); + putop(c, OP_BRANCH, -LABEL_LOOPSTART); + label(c, LABEL_LOOPBREAK); + putop(c, OP_POP); /* Packed and non-packed join. */ + maybeput(c, OP_ENDSEQ, h, f, UPB_HANDLER_ENDSEQ); + putop(c, OP_SETDELIM); /* Could remove for non-packed by dup ENDSEQ. */ + } else { + putop(c, OP_CHECKDELIM, LABEL_ENDMSG); + putchecktag(c, f, wire_type, LABEL_DISPATCH); + dispatchtarget(c, method, f, wire_type); + putop(c, parse_type, sel); + } +} + +/* Adds bytecode for parsing the given message to the given decoderplan, + * while adding all dispatch targets to this message's dispatch table. */ +static void compile_method(compiler *c, upb_pbdecodermethod *method) { + const upb_handlers *h; + const upb_msgdef *md; + uint32_t* start_pc; + upb_msg_field_iter i; + upb_value val; + + assert(method); + + /* Clear all entries in the dispatch table. */ + upb_inttable_uninit(&method->dispatch); + upb_inttable_init(&method->dispatch, UPB_CTYPE_UINT64); + + h = upb_pbdecodermethod_desthandlers(method); + md = upb_handlers_msgdef(h); + + method->code_base.ofs = pcofs(c); + putop(c, OP_SETDISPATCH, &method->dispatch); + putsel(c, OP_STARTMSG, UPB_STARTMSG_SELECTOR, h); + label(c, LABEL_FIELD); + start_pc = c->pc; + for(upb_msg_field_begin(&i, md); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { + const upb_fielddef *f = upb_msg_iter_field(&i); + upb_fieldtype_t type = upb_fielddef_type(f); + + if (type == UPB_TYPE_MESSAGE && !(haslazyhandlers(h, f) && c->lazy)) { + generate_msgfield(c, f, method); + } else if (type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES || + type == UPB_TYPE_MESSAGE) { + generate_delimfield(c, f, method); + } else { + generate_primitivefield(c, f, method); + } + } + + /* If there were no fields, or if no handlers were defined, we need to + * generate a non-empty loop body so that we can at least dispatch for unknown + * fields and check for the end of the message. */ + if (c->pc == start_pc) { + /* Check for end-of-message. */ + putop(c, OP_CHECKDELIM, LABEL_ENDMSG); + /* Unconditionally dispatch. */ + putop(c, OP_DISPATCH, 0); + } + + /* For now we just loop back to the last field of the message (or if none, + * the DISPATCH opcode for the message). */ + putop(c, OP_BRANCH, -LABEL_FIELD); + + /* Insert both a label and a dispatch table entry for this end-of-msg. */ + label(c, LABEL_ENDMSG); + val = upb_value_uint64(pcofs(c) - method->code_base.ofs); + upb_inttable_insert(&method->dispatch, DISPATCH_ENDMSG, val); + + putsel(c, OP_ENDMSG, UPB_ENDMSG_SELECTOR, h); + putop(c, OP_RET); + + upb_inttable_compact(&method->dispatch); +} + +/* Populate "methods" with new upb_pbdecodermethod objects reachable from "h". + * Returns the method for these handlers. + * + * Generates a new method for every destination handlers reachable from "h". */ +static void find_methods(compiler *c, const upb_handlers *h) { + upb_value v; + upb_msg_field_iter i; + const upb_msgdef *md; + + if (upb_inttable_lookupptr(&c->group->methods, h, &v)) + return; + newmethod(h, c->group); + + /* Find submethods. */ + md = upb_handlers_msgdef(h); + for(upb_msg_field_begin(&i, md); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { + const upb_fielddef *f = upb_msg_iter_field(&i); + const upb_handlers *sub_h; + if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE && + (sub_h = upb_handlers_getsubhandlers(h, f)) != NULL) { + /* We only generate a decoder method for submessages with handlers. + * Others will be parsed as unknown fields. */ + find_methods(c, sub_h); + } + } +} + +/* (Re-)compile bytecode for all messages in "msgs." + * Overwrites any existing bytecode in "c". */ +static void compile_methods(compiler *c) { + upb_inttable_iter i; + + /* Start over at the beginning of the bytecode. */ + c->pc = c->group->bytecode; + + upb_inttable_begin(&i, &c->group->methods); + for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { + upb_pbdecodermethod *method = upb_value_getptr(upb_inttable_iter_value(&i)); + compile_method(c, method); + } +} + +static void set_bytecode_handlers(mgroup *g) { + upb_inttable_iter i; + upb_inttable_begin(&i, &g->methods); + for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { + upb_pbdecodermethod *m = upb_value_getptr(upb_inttable_iter_value(&i)); + upb_byteshandler *h = &m->input_handler_; + + m->code_base.ptr = g->bytecode + m->code_base.ofs; + + upb_byteshandler_setstartstr(h, upb_pbdecoder_startbc, m->code_base.ptr); + upb_byteshandler_setstring(h, upb_pbdecoder_decode, g); + upb_byteshandler_setendstr(h, upb_pbdecoder_end, m); + } +} + + +/* JIT setup. *****************************************************************/ + +#ifdef UPB_USE_JIT_X64 + +static void sethandlers(mgroup *g, bool allowjit) { + g->jit_code = NULL; + if (allowjit) { + /* Compile byte-code into machine code, create handlers. */ + upb_pbdecoder_jit(g); + } else { + set_bytecode_handlers(g); + } +} + +#else /* UPB_USE_JIT_X64 */ + +static void sethandlers(mgroup *g, bool allowjit) { + /* No JIT compiled in; use bytecode handlers unconditionally. */ + UPB_UNUSED(allowjit); + set_bytecode_handlers(g); +} + +#endif /* UPB_USE_JIT_X64 */ + + +/* TODO(haberman): allow this to be constructed for an arbitrary set of dest + * handlers and other mgroups (but verify we have a transitive closure). */ +const mgroup *mgroup_new(const upb_handlers *dest, bool allowjit, bool lazy, + const void *owner) { + mgroup *g; + compiler *c; + + UPB_UNUSED(allowjit); + assert(upb_handlers_isfrozen(dest)); + + g = newgroup(owner); + c = newcompiler(g, lazy); + find_methods(c, dest); + + /* We compile in two passes: + * 1. all messages are assigned relative offsets from the beginning of the + * bytecode (saved in method->code_base). + * 2. forwards OP_CALL instructions can be correctly linked since message + * offsets have been previously assigned. + * + * Could avoid the second pass by linking OP_CALL instructions somehow. */ + compile_methods(c); + compile_methods(c); + g->bytecode_end = c->pc; + freecompiler(c); + +#ifdef UPB_DUMP_BYTECODE + { + FILE *f = fopen("/tmp/upb-bytecode", "wb"); + assert(f); + dumpbc(g->bytecode, g->bytecode_end, stderr); + dumpbc(g->bytecode, g->bytecode_end, f); + fclose(f); + } +#endif + + sethandlers(g, allowjit); + return g; +} + + +/* upb_pbcodecache ************************************************************/ + +void upb_pbcodecache_init(upb_pbcodecache *c) { + upb_inttable_init(&c->groups, UPB_CTYPE_CONSTPTR); + c->allow_jit_ = true; +} + +void upb_pbcodecache_uninit(upb_pbcodecache *c) { + upb_inttable_iter i; + upb_inttable_begin(&i, &c->groups); + for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { + const mgroup *group = upb_value_getconstptr(upb_inttable_iter_value(&i)); + mgroup_unref(group, c); + } + upb_inttable_uninit(&c->groups); +} + +bool upb_pbcodecache_allowjit(const upb_pbcodecache *c) { + return c->allow_jit_; +} + +bool upb_pbcodecache_setallowjit(upb_pbcodecache *c, bool allow) { + if (upb_inttable_count(&c->groups) > 0) + return false; + c->allow_jit_ = allow; + return true; +} + +const upb_pbdecodermethod *upb_pbcodecache_getdecodermethod( + upb_pbcodecache *c, const upb_pbdecodermethodopts *opts) { + upb_value v; + bool ok; + + /* Right now we build a new DecoderMethod every time. + * TODO(haberman): properly cache methods by their true key. */ + const mgroup *g = mgroup_new(opts->handlers, c->allow_jit_, opts->lazy, c); + upb_inttable_push(&c->groups, upb_value_constptr(g)); + + ok = upb_inttable_lookupptr(&g->methods, opts->handlers, &v); + UPB_ASSERT_VAR(ok, ok); + return upb_value_getptr(v); +} + + +/* upb_pbdecodermethodopts ****************************************************/ + +void upb_pbdecodermethodopts_init(upb_pbdecodermethodopts *opts, + const upb_handlers *h) { + opts->handlers = h; + opts->lazy = false; +} + +void upb_pbdecodermethodopts_setlazy(upb_pbdecodermethodopts *opts, bool lazy) { + opts->lazy = lazy; +} +/* +** upb::Decoder (Bytecode Decoder VM) +** +** Bytecode must previously have been generated using the bytecode compiler in +** compile_decoder.c. This decoder then walks through the bytecode op-by-op to +** parse the input. +** +** Decoding is fully resumable; we just keep a pointer to the current bytecode +** instruction and resume from there. A fair amount of the logic here is to +** handle the fact that values can span buffer seams and we have to be able to +** be capable of suspending/resuming from any byte in the stream. This +** sometimes requires keeping a few trailing bytes from the last buffer around +** in the "residual" buffer. +*/ + +#include +#include + +#ifdef UPB_DUMP_BYTECODE +#include +#endif + +#define CHECK_SUSPEND(x) if (!(x)) return upb_pbdecoder_suspend(d); + +/* Error messages that are shared between the bytecode and JIT decoders. */ +const char *kPbDecoderStackOverflow = "Nesting too deep."; +const char *kPbDecoderSubmessageTooLong = + "Submessage end extends past enclosing submessage."; + +/* Error messages shared within this file. */ +static const char *kUnterminatedVarint = "Unterminated varint."; + +/* upb_pbdecoder **************************************************************/ + +static opcode halt = OP_HALT; + +/* Whether an op consumes any of the input buffer. */ +static bool consumes_input(opcode op) { + switch (op) { + case OP_SETDISPATCH: + case OP_STARTMSG: + case OP_ENDMSG: + case OP_STARTSEQ: + case OP_ENDSEQ: + case OP_STARTSUBMSG: + case OP_ENDSUBMSG: + case OP_STARTSTR: + case OP_ENDSTR: + case OP_PUSHTAGDELIM: + case OP_POP: + case OP_SETDELIM: + case OP_SETBIGGROUPNUM: + case OP_CHECKDELIM: + case OP_CALL: + case OP_RET: + case OP_BRANCH: + return false; + default: + return true; + } +} + +static size_t stacksize(upb_pbdecoder *d, size_t entries) { + UPB_UNUSED(d); + return entries * sizeof(upb_pbdecoder_frame); +} + +static size_t callstacksize(upb_pbdecoder *d, size_t entries) { + UPB_UNUSED(d); + +#ifdef UPB_USE_JIT_X64 + if (d->method_->is_native_) { + /* Each native stack frame needs two pointers, plus we need a few frames for + * the enter/exit trampolines. */ + size_t ret = entries * sizeof(void*) * 2; + ret += sizeof(void*) * 10; + return ret; + } +#endif + + return entries * sizeof(uint32_t*); +} + + +static bool in_residual_buf(const upb_pbdecoder *d, const char *p); + +/* It's unfortunate that we have to micro-manage the compiler with + * UPB_FORCEINLINE and UPB_NOINLINE, especially since this tuning is necessarily + * specific to one hardware configuration. But empirically on a Core i7, + * performance increases 30-50% with these annotations. Every instance where + * these appear, gcc 4.2.1 made the wrong decision and degraded performance in + * benchmarks. */ + +static void seterr(upb_pbdecoder *d, const char *msg) { + upb_status status = UPB_STATUS_INIT; + upb_status_seterrmsg(&status, msg); + upb_env_reporterror(d->env, &status); +} + +void upb_pbdecoder_seterr(upb_pbdecoder *d, const char *msg) { + seterr(d, msg); +} + + +/* Buffering ******************************************************************/ + +/* We operate on one buffer at a time, which is either the user's buffer passed + * to our "decode" callback or some residual bytes from the previous buffer. */ + +/* How many bytes can be safely read from d->ptr without reading past end-of-buf + * or past the current delimited end. */ +static size_t curbufleft(const upb_pbdecoder *d) { + assert(d->data_end >= d->ptr); + return d->data_end - d->ptr; +} + +/* How many bytes are available before end-of-buffer. */ +static size_t bufleft(const upb_pbdecoder *d) { + return d->end - d->ptr; +} + +/* Overall stream offset of d->ptr. */ +uint64_t offset(const upb_pbdecoder *d) { + return d->bufstart_ofs + (d->ptr - d->buf); +} + +/* How many bytes are available before the end of this delimited region. */ +size_t delim_remaining(const upb_pbdecoder *d) { + return d->top->end_ofs - offset(d); +} + +/* Advances d->ptr. */ +static void advance(upb_pbdecoder *d, size_t len) { + assert(curbufleft(d) >= len); + d->ptr += len; +} + +static bool in_buf(const char *p, const char *buf, const char *end) { + return p >= buf && p <= end; +} + +static bool in_residual_buf(const upb_pbdecoder *d, const char *p) { + return in_buf(p, d->residual, d->residual_end); +} + +/* Calculates the delim_end value, which is affected by both the current buffer + * and the parsing stack, so must be called whenever either is updated. */ +static void set_delim_end(upb_pbdecoder *d) { + size_t delim_ofs = d->top->end_ofs - d->bufstart_ofs; + if (delim_ofs <= (size_t)(d->end - d->buf)) { + d->delim_end = d->buf + delim_ofs; + d->data_end = d->delim_end; + } else { + d->data_end = d->end; + d->delim_end = NULL; + } +} + +static void switchtobuf(upb_pbdecoder *d, const char *buf, const char *end) { + d->ptr = buf; + d->buf = buf; + d->end = end; + set_delim_end(d); +} + +static void advancetobuf(upb_pbdecoder *d, const char *buf, size_t len) { + assert(curbufleft(d) == 0); + d->bufstart_ofs += (d->end - d->buf); + switchtobuf(d, buf, buf + len); +} + +static void checkpoint(upb_pbdecoder *d) { + /* The assertion here is in the interests of efficiency, not correctness. + * We are trying to ensure that we don't checkpoint() more often than + * necessary. */ + assert(d->checkpoint != d->ptr); + d->checkpoint = d->ptr; +} + +/* Skips "bytes" bytes in the stream, which may be more than available. If we + * skip more bytes than are available, we return a long read count to the caller + * indicating how many bytes can be skipped over before passing actual data + * again. Skipped bytes can pass a NULL buffer and the decoder guarantees they + * won't actually be read. + */ +static int32_t skip(upb_pbdecoder *d, size_t bytes) { + assert(!in_residual_buf(d, d->ptr) || d->size_param == 0); + assert(d->skip == 0); + if (bytes > delim_remaining(d)) { + seterr(d, "Skipped value extended beyond enclosing submessage."); + return upb_pbdecoder_suspend(d); + } else if (bufleft(d) > bytes) { + /* Skipped data is all in current buffer, and more is still available. */ + advance(d, bytes); + d->skip = 0; + return DECODE_OK; + } else { + /* Skipped data extends beyond currently available buffers. */ + d->pc = d->last; + d->skip = bytes - curbufleft(d); + d->bufstart_ofs += (d->end - d->buf); + d->residual_end = d->residual; + switchtobuf(d, d->residual, d->residual_end); + return d->size_param + d->skip; + } +} + + +/* Resumes the decoder from an initial state or from a previous suspend. */ +int32_t upb_pbdecoder_resume(upb_pbdecoder *d, void *p, const char *buf, + size_t size, const upb_bufhandle *handle) { + UPB_UNUSED(p); /* Useless; just for the benefit of the JIT. */ + + d->buf_param = buf; + d->size_param = size; + d->handle = handle; + + if (d->residual_end > d->residual) { + /* We have residual bytes from the last buffer. */ + assert(d->ptr == d->residual); + } else { + switchtobuf(d, buf, buf + size); + } + + d->checkpoint = d->ptr; + + if (d->skip) { + size_t skip_bytes = d->skip; + d->skip = 0; + CHECK_RETURN(skip(d, skip_bytes)); + d->checkpoint = d->ptr; + } + + if (!buf) { + /* NULL buf is ok if its entire span is covered by the "skip" above, but + * by this point we know that "skip" doesn't cover the buffer. */ + seterr(d, "Passed NULL buffer over non-skippable region."); + return upb_pbdecoder_suspend(d); + } + + if (d->top->groupnum < 0) { + CHECK_RETURN(upb_pbdecoder_skipunknown(d, -1, 0)); + d->checkpoint = d->ptr; + } + + return DECODE_OK; +} + +/* Suspends the decoder at the last checkpoint, without saving any residual + * bytes. If there are any unconsumed bytes, returns a short byte count. */ +size_t upb_pbdecoder_suspend(upb_pbdecoder *d) { + d->pc = d->last; + if (d->checkpoint == d->residual) { + /* Checkpoint was in residual buf; no user bytes were consumed. */ + d->ptr = d->residual; + return 0; + } else { + size_t consumed; + assert(!in_residual_buf(d, d->checkpoint)); + assert(d->buf == d->buf_param); + + consumed = d->checkpoint - d->buf; + d->bufstart_ofs += consumed; + d->residual_end = d->residual; + switchtobuf(d, d->residual, d->residual_end); + return consumed; + } +} + +/* Suspends the decoder at the last checkpoint, and saves any unconsumed + * bytes in our residual buffer. This is necessary if we need more user + * bytes to form a complete value, which might not be contiguous in the + * user's buffers. Always consumes all user bytes. */ +static size_t suspend_save(upb_pbdecoder *d) { + /* We hit end-of-buffer before we could parse a full value. + * Save any unconsumed bytes (if any) to the residual buffer. */ + d->pc = d->last; + + if (d->checkpoint == d->residual) { + /* Checkpoint was in residual buf; append user byte(s) to residual buf. */ + assert((d->residual_end - d->residual) + d->size_param <= + sizeof(d->residual)); + if (!in_residual_buf(d, d->ptr)) { + d->bufstart_ofs -= (d->residual_end - d->residual); + } + memcpy(d->residual_end, d->buf_param, d->size_param); + d->residual_end += d->size_param; + } else { + /* Checkpoint was in user buf; old residual bytes not needed. */ + size_t save; + assert(!in_residual_buf(d, d->checkpoint)); + + d->ptr = d->checkpoint; + save = curbufleft(d); + assert(save <= sizeof(d->residual)); + memcpy(d->residual, d->ptr, save); + d->residual_end = d->residual + save; + d->bufstart_ofs = offset(d); + } + + switchtobuf(d, d->residual, d->residual_end); + return d->size_param; +} + +/* Copies the next "bytes" bytes into "buf" and advances the stream. + * Requires that this many bytes are available in the current buffer. */ +UPB_FORCEINLINE static void consumebytes(upb_pbdecoder *d, void *buf, + size_t bytes) { + assert(bytes <= curbufleft(d)); + memcpy(buf, d->ptr, bytes); + advance(d, bytes); +} + +/* Slow path for getting the next "bytes" bytes, regardless of whether they are + * available in the current buffer or not. Returns a status code as described + * in decoder.int.h. */ +UPB_NOINLINE static int32_t getbytes_slow(upb_pbdecoder *d, void *buf, + size_t bytes) { + const size_t avail = curbufleft(d); + consumebytes(d, buf, avail); + bytes -= avail; + assert(bytes > 0); + if (in_residual_buf(d, d->ptr)) { + advancetobuf(d, d->buf_param, d->size_param); + } + if (curbufleft(d) >= bytes) { + consumebytes(d, (char *)buf + avail, bytes); + return DECODE_OK; + } else if (d->data_end == d->delim_end) { + seterr(d, "Submessage ended in the middle of a value or group"); + return upb_pbdecoder_suspend(d); + } else { + return suspend_save(d); + } +} + +/* Gets the next "bytes" bytes, regardless of whether they are available in the + * current buffer or not. Returns a status code as described in decoder.int.h. + */ +UPB_FORCEINLINE static int32_t getbytes(upb_pbdecoder *d, void *buf, + size_t bytes) { + if (curbufleft(d) >= bytes) { + /* Buffer has enough data to satisfy. */ + consumebytes(d, buf, bytes); + return DECODE_OK; + } else { + return getbytes_slow(d, buf, bytes); + } +} + +UPB_NOINLINE static size_t peekbytes_slow(upb_pbdecoder *d, void *buf, + size_t bytes) { + size_t ret = curbufleft(d); + memcpy(buf, d->ptr, ret); + if (in_residual_buf(d, d->ptr)) { + size_t copy = UPB_MIN(bytes - ret, d->size_param); + memcpy((char *)buf + ret, d->buf_param, copy); + ret += copy; + } + return ret; +} + +UPB_FORCEINLINE static size_t peekbytes(upb_pbdecoder *d, void *buf, + size_t bytes) { + if (curbufleft(d) >= bytes) { + memcpy(buf, d->ptr, bytes); + return bytes; + } else { + return peekbytes_slow(d, buf, bytes); + } +} + + +/* Decoding of wire types *****************************************************/ + +/* Slow path for decoding a varint from the current buffer position. + * Returns a status code as described in decoder.int.h. */ +UPB_NOINLINE int32_t upb_pbdecoder_decode_varint_slow(upb_pbdecoder *d, + uint64_t *u64) { + uint8_t byte = 0x80; + int bitpos; + *u64 = 0; + for(bitpos = 0; bitpos < 70 && (byte & 0x80); bitpos += 7) { + int32_t ret = getbytes(d, &byte, 1); + if (ret >= 0) return ret; + *u64 |= (uint64_t)(byte & 0x7F) << bitpos; + } + if(bitpos == 70 && (byte & 0x80)) { + seterr(d, kUnterminatedVarint); + return upb_pbdecoder_suspend(d); + } + return DECODE_OK; +} + +/* Decodes a varint from the current buffer position. + * Returns a status code as described in decoder.int.h. */ +UPB_FORCEINLINE static int32_t decode_varint(upb_pbdecoder *d, uint64_t *u64) { + if (curbufleft(d) > 0 && !(*d->ptr & 0x80)) { + *u64 = *d->ptr; + advance(d, 1); + return DECODE_OK; + } else if (curbufleft(d) >= 10) { + /* Fast case. */ + upb_decoderet r = upb_vdecode_fast(d->ptr); + if (r.p == NULL) { + seterr(d, kUnterminatedVarint); + return upb_pbdecoder_suspend(d); + } + advance(d, r.p - d->ptr); + *u64 = r.val; + return DECODE_OK; + } else { + /* Slow case -- varint spans buffer seam. */ + return upb_pbdecoder_decode_varint_slow(d, u64); + } +} + +/* Decodes a 32-bit varint from the current buffer position. + * Returns a status code as described in decoder.int.h. */ +UPB_FORCEINLINE static int32_t decode_v32(upb_pbdecoder *d, uint32_t *u32) { + uint64_t u64; + int32_t ret = decode_varint(d, &u64); + if (ret >= 0) return ret; + if (u64 > UINT32_MAX) { + seterr(d, "Unterminated 32-bit varint"); + /* TODO(haberman) guarantee that this function return is >= 0 somehow, + * so we know this path will always be treated as error by our caller. + * Right now the size_t -> int32_t can overflow and produce negative values. + */ + *u32 = 0; + return upb_pbdecoder_suspend(d); + } + *u32 = u64; + return DECODE_OK; +} + +/* Decodes a fixed32 from the current buffer position. + * Returns a status code as described in decoder.int.h. + * TODO: proper byte swapping for big-endian machines. */ +UPB_FORCEINLINE static int32_t decode_fixed32(upb_pbdecoder *d, uint32_t *u32) { + return getbytes(d, u32, 4); +} + +/* Decodes a fixed64 from the current buffer position. + * Returns a status code as described in decoder.int.h. + * TODO: proper byte swapping for big-endian machines. */ +UPB_FORCEINLINE static int32_t decode_fixed64(upb_pbdecoder *d, uint64_t *u64) { + return getbytes(d, u64, 8); +} + +/* Non-static versions of the above functions. + * These are called by the JIT for fallback paths. */ +int32_t upb_pbdecoder_decode_f32(upb_pbdecoder *d, uint32_t *u32) { + return decode_fixed32(d, u32); +} + +int32_t upb_pbdecoder_decode_f64(upb_pbdecoder *d, uint64_t *u64) { + return decode_fixed64(d, u64); +} + +static double as_double(uint64_t n) { double d; memcpy(&d, &n, 8); return d; } +static float as_float(uint32_t n) { float f; memcpy(&f, &n, 4); return f; } + +/* Pushes a frame onto the decoder stack. */ +static bool decoder_push(upb_pbdecoder *d, uint64_t end) { + upb_pbdecoder_frame *fr = d->top; + + if (end > fr->end_ofs) { + seterr(d, kPbDecoderSubmessageTooLong); + return false; + } else if (fr == d->limit) { + seterr(d, kPbDecoderStackOverflow); + return false; + } + + fr++; + fr->end_ofs = end; + fr->dispatch = NULL; + fr->groupnum = 0; + d->top = fr; + return true; +} + +static bool pushtagdelim(upb_pbdecoder *d, uint32_t arg) { + /* While we expect to see an "end" tag (either ENDGROUP or a non-sequence + * field number) prior to hitting any enclosing submessage end, pushing our + * existing delim end prevents us from continuing to parse values from a + * corrupt proto that doesn't give us an END tag in time. */ + if (!decoder_push(d, d->top->end_ofs)) + return false; + d->top->groupnum = arg; + return true; +} + +/* Pops a frame from the decoder stack. */ +static void decoder_pop(upb_pbdecoder *d) { d->top--; } + +UPB_NOINLINE int32_t upb_pbdecoder_checktag_slow(upb_pbdecoder *d, + uint64_t expected) { + uint64_t data = 0; + size_t bytes = upb_value_size(expected); + size_t read = peekbytes(d, &data, bytes); + if (read == bytes && data == expected) { + /* Advance past matched bytes. */ + int32_t ok = getbytes(d, &data, read); + UPB_ASSERT_VAR(ok, ok < 0); + return DECODE_OK; + } else if (read < bytes && memcmp(&data, &expected, read) == 0) { + return suspend_save(d); + } else { + return DECODE_MISMATCH; + } +} + +int32_t upb_pbdecoder_skipunknown(upb_pbdecoder *d, int32_t fieldnum, + uint8_t wire_type) { + if (fieldnum >= 0) + goto have_tag; + + while (true) { + uint32_t tag; + CHECK_RETURN(decode_v32(d, &tag)); + wire_type = tag & 0x7; + fieldnum = tag >> 3; + +have_tag: + if (fieldnum == 0) { + seterr(d, "Saw invalid field number (0)"); + return upb_pbdecoder_suspend(d); + } + + /* TODO: deliver to unknown field callback. */ + switch (wire_type) { + case UPB_WIRE_TYPE_32BIT: + CHECK_RETURN(skip(d, 4)); + break; + case UPB_WIRE_TYPE_64BIT: + CHECK_RETURN(skip(d, 8)); + break; + case UPB_WIRE_TYPE_VARINT: { + uint64_t u64; + CHECK_RETURN(decode_varint(d, &u64)); + break; + } + case UPB_WIRE_TYPE_DELIMITED: { + uint32_t len; + CHECK_RETURN(decode_v32(d, &len)); + CHECK_RETURN(skip(d, len)); + break; + } + case UPB_WIRE_TYPE_START_GROUP: + CHECK_SUSPEND(pushtagdelim(d, -fieldnum)); + break; + case UPB_WIRE_TYPE_END_GROUP: + if (fieldnum == -d->top->groupnum) { + decoder_pop(d); + } else if (fieldnum == d->top->groupnum) { + return DECODE_ENDGROUP; + } else { + seterr(d, "Unmatched ENDGROUP tag."); + return upb_pbdecoder_suspend(d); + } + break; + default: + seterr(d, "Invalid wire type"); + return upb_pbdecoder_suspend(d); + } + + if (d->top->groupnum >= 0) { + return DECODE_OK; + } + + /* Unknown group -- continue looping over unknown fields. */ + checkpoint(d); + } +} + +static void goto_endmsg(upb_pbdecoder *d) { + upb_value v; + bool found = upb_inttable_lookup32(d->top->dispatch, DISPATCH_ENDMSG, &v); + UPB_ASSERT_VAR(found, found); + d->pc = d->top->base + upb_value_getuint64(v); +} + +/* Parses a tag and jumps to the corresponding bytecode instruction for this + * field. + * + * If the tag is unknown (or the wire type doesn't match), parses the field as + * unknown. If the tag is a valid ENDGROUP tag, jumps to the bytecode + * instruction for the end of message. */ +static int32_t dispatch(upb_pbdecoder *d) { + upb_inttable *dispatch = d->top->dispatch; + uint32_t tag; + uint8_t wire_type; + uint32_t fieldnum; + upb_value val; + int32_t retval; + + /* Decode tag. */ + CHECK_RETURN(decode_v32(d, &tag)); + wire_type = tag & 0x7; + fieldnum = tag >> 3; + + /* Lookup tag. Because of packed/non-packed compatibility, we have to + * check the wire type against two possibilities. */ + if (fieldnum != DISPATCH_ENDMSG && + upb_inttable_lookup32(dispatch, fieldnum, &val)) { + uint64_t v = upb_value_getuint64(val); + if (wire_type == (v & 0xff)) { + d->pc = d->top->base + (v >> 16); + return DECODE_OK; + } else if (wire_type == ((v >> 8) & 0xff)) { + bool found = + upb_inttable_lookup(dispatch, fieldnum + UPB_MAX_FIELDNUMBER, &val); + UPB_ASSERT_VAR(found, found); + d->pc = d->top->base + upb_value_getuint64(val); + return DECODE_OK; + } + } + + /* We have some unknown fields (or ENDGROUP) to parse. The DISPATCH or TAG + * bytecode that triggered this is preceded by a CHECKDELIM bytecode which + * we need to back up to, so that when we're done skipping unknown data we + * can re-check the delimited end. */ + d->last--; /* Necessary if we get suspended */ + d->pc = d->last; + assert(getop(*d->last) == OP_CHECKDELIM); + + /* Unknown field or ENDGROUP. */ + retval = upb_pbdecoder_skipunknown(d, fieldnum, wire_type); + + CHECK_RETURN(retval); + + if (retval == DECODE_ENDGROUP) { + goto_endmsg(d); + return DECODE_OK; + } + + return DECODE_OK; +} + +/* Callers know that the stack is more than one deep because the opcodes that + * call this only occur after PUSH operations. */ +upb_pbdecoder_frame *outer_frame(upb_pbdecoder *d) { + assert(d->top != d->stack); + return d->top - 1; +} + + +/* The main decoding loop *****************************************************/ + +/* The main decoder VM function. Uses traditional bytecode dispatch loop with a + * switch() statement. */ +size_t run_decoder_vm(upb_pbdecoder *d, const mgroup *group, + const upb_bufhandle* handle) { + +#define VMCASE(op, code) \ + case op: { code; if (consumes_input(op)) checkpoint(d); break; } +#define PRIMITIVE_OP(type, wt, name, convfunc, ctype) \ + VMCASE(OP_PARSE_ ## type, { \ + ctype val; \ + CHECK_RETURN(decode_ ## wt(d, &val)); \ + upb_sink_put ## name(&d->top->sink, arg, (convfunc)(val)); \ + }) + + while(1) { + int32_t instruction; + opcode op; + uint32_t arg; + int32_t longofs; + + d->last = d->pc; + instruction = *d->pc++; + op = getop(instruction); + arg = instruction >> 8; + longofs = arg; + assert(d->ptr != d->residual_end); + UPB_UNUSED(group); +#ifdef UPB_DUMP_BYTECODE + fprintf(stderr, "s_ofs=%d buf_ofs=%d data_rem=%d buf_rem=%d delim_rem=%d " + "%x %s (%d)\n", + (int)offset(d), + (int)(d->ptr - d->buf), + (int)(d->data_end - d->ptr), + (int)(d->end - d->ptr), + (int)((d->top->end_ofs - d->bufstart_ofs) - (d->ptr - d->buf)), + (int)(d->pc - 1 - group->bytecode), + upb_pbdecoder_getopname(op), + arg); +#endif + switch (op) { + /* Technically, we are losing data if we see a 32-bit varint that is not + * properly sign-extended. We could detect this and error about the data + * loss, but proto2 does not do this, so we pass. */ + PRIMITIVE_OP(INT32, varint, int32, int32_t, uint64_t) + PRIMITIVE_OP(INT64, varint, int64, int64_t, uint64_t) + PRIMITIVE_OP(UINT32, varint, uint32, uint32_t, uint64_t) + PRIMITIVE_OP(UINT64, varint, uint64, uint64_t, uint64_t) + PRIMITIVE_OP(FIXED32, fixed32, uint32, uint32_t, uint32_t) + PRIMITIVE_OP(FIXED64, fixed64, uint64, uint64_t, uint64_t) + PRIMITIVE_OP(SFIXED32, fixed32, int32, int32_t, uint32_t) + PRIMITIVE_OP(SFIXED64, fixed64, int64, int64_t, uint64_t) + PRIMITIVE_OP(BOOL, varint, bool, bool, uint64_t) + PRIMITIVE_OP(DOUBLE, fixed64, double, as_double, uint64_t) + PRIMITIVE_OP(FLOAT, fixed32, float, as_float, uint32_t) + PRIMITIVE_OP(SINT32, varint, int32, upb_zzdec_32, uint64_t) + PRIMITIVE_OP(SINT64, varint, int64, upb_zzdec_64, uint64_t) + + VMCASE(OP_SETDISPATCH, + d->top->base = d->pc - 1; + memcpy(&d->top->dispatch, d->pc, sizeof(void*)); + d->pc += sizeof(void*) / sizeof(uint32_t); + ) + VMCASE(OP_STARTMSG, + CHECK_SUSPEND(upb_sink_startmsg(&d->top->sink)); + ) + VMCASE(OP_ENDMSG, + CHECK_SUSPEND(upb_sink_endmsg(&d->top->sink, d->status)); + ) + VMCASE(OP_STARTSEQ, + upb_pbdecoder_frame *outer = outer_frame(d); + CHECK_SUSPEND(upb_sink_startseq(&outer->sink, arg, &d->top->sink)); + ) + VMCASE(OP_ENDSEQ, + CHECK_SUSPEND(upb_sink_endseq(&d->top->sink, arg)); + ) + VMCASE(OP_STARTSUBMSG, + upb_pbdecoder_frame *outer = outer_frame(d); + CHECK_SUSPEND(upb_sink_startsubmsg(&outer->sink, arg, &d->top->sink)); + ) + VMCASE(OP_ENDSUBMSG, + CHECK_SUSPEND(upb_sink_endsubmsg(&d->top->sink, arg)); + ) + VMCASE(OP_STARTSTR, + uint32_t len = delim_remaining(d); + upb_pbdecoder_frame *outer = outer_frame(d); + CHECK_SUSPEND(upb_sink_startstr(&outer->sink, arg, len, &d->top->sink)); + if (len == 0) { + d->pc++; /* Skip OP_STRING. */ + } + ) + VMCASE(OP_STRING, + uint32_t len = curbufleft(d); + size_t n = upb_sink_putstring(&d->top->sink, arg, d->ptr, len, handle); + if (n > len) { + if (n > delim_remaining(d)) { + seterr(d, "Tried to skip past end of string."); + return upb_pbdecoder_suspend(d); + } else { + int32_t ret = skip(d, n); + /* This shouldn't return DECODE_OK, because n > len. */ + assert(ret >= 0); + return ret; + } + } + advance(d, n); + if (n < len || d->delim_end == NULL) { + /* We aren't finished with this string yet. */ + d->pc--; /* Repeat OP_STRING. */ + if (n > 0) checkpoint(d); + return upb_pbdecoder_suspend(d); + } + ) + VMCASE(OP_ENDSTR, + CHECK_SUSPEND(upb_sink_endstr(&d->top->sink, arg)); + ) + VMCASE(OP_PUSHTAGDELIM, + CHECK_SUSPEND(pushtagdelim(d, arg)); + ) + VMCASE(OP_SETBIGGROUPNUM, + d->top->groupnum = *d->pc++; + ) + VMCASE(OP_POP, + assert(d->top > d->stack); + decoder_pop(d); + ) + VMCASE(OP_PUSHLENDELIM, + uint32_t len; + CHECK_RETURN(decode_v32(d, &len)); + CHECK_SUSPEND(decoder_push(d, offset(d) + len)); + set_delim_end(d); + ) + VMCASE(OP_SETDELIM, + set_delim_end(d); + ) + VMCASE(OP_CHECKDELIM, + /* We are guaranteed of this assert because we never allow ourselves to + * consume bytes beyond data_end, which covers delim_end when non-NULL. + */ + assert(!(d->delim_end && d->ptr > d->delim_end)); + if (d->ptr == d->delim_end) + d->pc += longofs; + ) + VMCASE(OP_CALL, + d->callstack[d->call_len++] = d->pc; + d->pc += longofs; + ) + VMCASE(OP_RET, + assert(d->call_len > 0); + d->pc = d->callstack[--d->call_len]; + ) + VMCASE(OP_BRANCH, + d->pc += longofs; + ) + VMCASE(OP_TAG1, + uint8_t expected; + CHECK_SUSPEND(curbufleft(d) > 0); + expected = (arg >> 8) & 0xff; + if (*d->ptr == expected) { + advance(d, 1); + } else { + int8_t shortofs; + badtag: + shortofs = arg; + if (shortofs == LABEL_DISPATCH) { + CHECK_RETURN(dispatch(d)); + } else { + d->pc += shortofs; + break; /* Avoid checkpoint(). */ + } + } + ) + VMCASE(OP_TAG2, + uint16_t expected; + CHECK_SUSPEND(curbufleft(d) > 0); + expected = (arg >> 8) & 0xffff; + if (curbufleft(d) >= 2) { + uint16_t actual; + memcpy(&actual, d->ptr, 2); + if (expected == actual) { + advance(d, 2); + } else { + goto badtag; + } + } else { + int32_t result = upb_pbdecoder_checktag_slow(d, expected); + if (result == DECODE_MISMATCH) goto badtag; + if (result >= 0) return result; + } + ) + VMCASE(OP_TAGN, { + uint64_t expected; + int32_t result; + memcpy(&expected, d->pc, 8); + d->pc += 2; + result = upb_pbdecoder_checktag_slow(d, expected); + if (result == DECODE_MISMATCH) goto badtag; + if (result >= 0) return result; + }) + VMCASE(OP_DISPATCH, { + CHECK_RETURN(dispatch(d)); + }) + VMCASE(OP_HALT, { + return d->size_param; + }) + } + } +} + + +/* BytesHandler handlers ******************************************************/ + +void *upb_pbdecoder_startbc(void *closure, const void *pc, size_t size_hint) { + upb_pbdecoder *d = closure; + UPB_UNUSED(size_hint); + d->top->end_ofs = UINT64_MAX; + d->bufstart_ofs = 0; + d->call_len = 1; + d->callstack[0] = &halt; + d->pc = pc; + d->skip = 0; + return d; +} + +void *upb_pbdecoder_startjit(void *closure, const void *hd, size_t size_hint) { + upb_pbdecoder *d = closure; + UPB_UNUSED(hd); + UPB_UNUSED(size_hint); + d->top->end_ofs = UINT64_MAX; + d->bufstart_ofs = 0; + d->call_len = 0; + d->skip = 0; + return d; +} + +bool upb_pbdecoder_end(void *closure, const void *handler_data) { + upb_pbdecoder *d = closure; + const upb_pbdecodermethod *method = handler_data; + uint64_t end; + char dummy; + + if (d->residual_end > d->residual) { + seterr(d, "Unexpected EOF: decoder still has buffered unparsed data"); + return false; + } + + if (d->skip) { + seterr(d, "Unexpected EOF inside skipped data"); + return false; + } + + if (d->top->end_ofs != UINT64_MAX) { + seterr(d, "Unexpected EOF inside delimited string"); + return false; + } + + /* The user's end() call indicates that the message ends here. */ + end = offset(d); + d->top->end_ofs = end; + +#ifdef UPB_USE_JIT_X64 + if (method->is_native_) { + const mgroup *group = (const mgroup*)method->group; + if (d->top != d->stack) + d->stack->end_ofs = 0; + group->jit_code(closure, method->code_base.ptr, &dummy, 0, NULL); + } else +#endif + { + const uint32_t *p = d->pc; + d->stack->end_ofs = end; + /* Check the previous bytecode, but guard against beginning. */ + if (p != method->code_base.ptr) p--; + if (getop(*p) == OP_CHECKDELIM) { + /* Rewind from OP_TAG* to OP_CHECKDELIM. */ + assert(getop(*d->pc) == OP_TAG1 || + getop(*d->pc) == OP_TAG2 || + getop(*d->pc) == OP_TAGN || + getop(*d->pc) == OP_DISPATCH); + d->pc = p; + } + upb_pbdecoder_decode(closure, handler_data, &dummy, 0, NULL); + } + + if (d->call_len != 0) { + seterr(d, "Unexpected EOF inside submessage or group"); + return false; + } + + return true; +} + +size_t upb_pbdecoder_decode(void *decoder, const void *group, const char *buf, + size_t size, const upb_bufhandle *handle) { + int32_t result = upb_pbdecoder_resume(decoder, NULL, buf, size, handle); + + if (result == DECODE_ENDGROUP) goto_endmsg(decoder); + CHECK_RETURN(result); + + return run_decoder_vm(decoder, group, handle); +} + + +/* Public API *****************************************************************/ + +void upb_pbdecoder_reset(upb_pbdecoder *d) { + d->top = d->stack; + d->top->groupnum = 0; + d->ptr = d->residual; + d->buf = d->residual; + d->end = d->residual; + d->residual_end = d->residual; +} + +upb_pbdecoder *upb_pbdecoder_create(upb_env *e, const upb_pbdecodermethod *m, + upb_sink *sink) { + const size_t default_max_nesting = 64; +#ifndef NDEBUG + size_t size_before = upb_env_bytesallocated(e); +#endif + + upb_pbdecoder *d = upb_env_malloc(e, sizeof(upb_pbdecoder)); + if (!d) return NULL; + + d->method_ = m; + d->callstack = upb_env_malloc(e, callstacksize(d, default_max_nesting)); + d->stack = upb_env_malloc(e, stacksize(d, default_max_nesting)); + if (!d->stack || !d->callstack) { + return NULL; + } + + d->env = e; + d->limit = d->stack + default_max_nesting - 1; + d->stack_size = default_max_nesting; + + upb_pbdecoder_reset(d); + upb_bytessink_reset(&d->input_, &m->input_handler_, d); + + assert(sink); + if (d->method_->dest_handlers_) { + if (sink->handlers != d->method_->dest_handlers_) + return NULL; + } + upb_sink_reset(&d->top->sink, sink->handlers, sink->closure); + + /* If this fails, increase the value in decoder.h. */ + assert(upb_env_bytesallocated(e) - size_before <= UPB_PB_DECODER_SIZE); + return d; +} + +uint64_t upb_pbdecoder_bytesparsed(const upb_pbdecoder *d) { + return offset(d); +} + +const upb_pbdecodermethod *upb_pbdecoder_method(const upb_pbdecoder *d) { + return d->method_; +} + +upb_bytessink *upb_pbdecoder_input(upb_pbdecoder *d) { + return &d->input_; +} + +size_t upb_pbdecoder_maxnesting(const upb_pbdecoder *d) { + return d->stack_size; +} + +bool upb_pbdecoder_setmaxnesting(upb_pbdecoder *d, size_t max) { + assert(d->top >= d->stack); + + if (max < (size_t)(d->top - d->stack)) { + /* Can't set a limit smaller than what we are currently at. */ + return false; + } + + if (max > d->stack_size) { + /* Need to reallocate stack and callstack to accommodate. */ + size_t old_size = stacksize(d, d->stack_size); + size_t new_size = stacksize(d, max); + void *p = upb_env_realloc(d->env, d->stack, old_size, new_size); + if (!p) { + return false; + } + d->stack = p; + + old_size = callstacksize(d, d->stack_size); + new_size = callstacksize(d, max); + p = upb_env_realloc(d->env, d->callstack, old_size, new_size); + if (!p) { + return false; + } + d->callstack = p; + + d->stack_size = max; + } + + d->limit = d->stack + max - 1; + return true; +} +/* +** upb::Encoder +** +** Since we are implementing pure handlers (ie. without any out-of-band access +** to pre-computed lengths), we have to buffer all submessages before we can +** emit even their first byte. +** +** Not knowing the size of submessages also means we can't write a perfect +** zero-copy implementation, even with buffering. Lengths are stored as +** varints, which means that we don't know how many bytes to reserve for the +** length until we know what the length is. +** +** This leaves us with three main choices: +** +** 1. buffer all submessage data in a temporary buffer, then copy it exactly +** once into the output buffer. +** +** 2. attempt to buffer data directly into the output buffer, estimating how +** many bytes each length will take. When our guesses are wrong, use +** memmove() to grow or shrink the allotted space. +** +** 3. buffer directly into the output buffer, allocating a max length +** ahead-of-time for each submessage length. If we overallocated, we waste +** space, but no memcpy() or memmove() is required. This approach requires +** defining a maximum size for submessages and rejecting submessages that +** exceed that size. +** +** (2) and (3) have the potential to have better performance, but they are more +** complicated and subtle to implement: +** +** (3) requires making an arbitrary choice of the maximum message size; it +** wastes space when submessages are shorter than this and fails +** completely when they are longer. This makes it more finicky and +** requires configuration based on the input. It also makes it impossible +** to perfectly match the output of reference encoders that always use the +** optimal amount of space for each length. +** +** (2) requires guessing the the size upfront, and if multiple lengths are +** guessed wrong the minimum required number of memmove() operations may +** be complicated to compute correctly. Implemented properly, it may have +** a useful amortized or average cost, but more investigation is required +** to determine this and what the optimal algorithm is to achieve it. +** +** (1) makes you always pay for exactly one copy, but its implementation is +** the simplest and its performance is predictable. +** +** So for now, we implement (1) only. If we wish to optimize later, we should +** be able to do it without affecting users. +** +** The strategy is to buffer the segments of data that do *not* depend on +** unknown lengths in one buffer, and keep a separate buffer of segment pointers +** and lengths. When the top-level submessage ends, we can go beginning to end, +** alternating the writing of lengths with memcpy() of the rest of the data. +** At the top level though, no buffering is required. +*/ + + +#include + +/* The output buffer is divided into segments; a segment is a string of data + * that is "ready to go" -- it does not need any varint lengths inserted into + * the middle. The seams between segments are where varints will be inserted + * once they are known. + * + * We also use the concept of a "run", which is a range of encoded bytes that + * occur at a single submessage level. Every segment contains one or more runs. + * + * A segment can span messages. Consider: + * + * .--Submessage lengths---------. + * | | | + * | V V + * V | |--------------- | |----------------- + * Submessages: | |----------------------------------------------- + * Top-level msg: ------------------------------------------------------------ + * + * Segments: ----- ------------------- ----------------- + * Runs: *---- *--------------*--- *---------------- + * (* marks the start) + * + * Note that the top-level menssage is not in any segment because it does not + * have any length preceding it. + * + * A segment is only interrupted when another length needs to be inserted. So + * observe how the second segment spans both the inner submessage and part of + * the next enclosing message. */ +typedef struct { + uint32_t msglen; /* The length to varint-encode before this segment. */ + uint32_t seglen; /* Length of the segment. */ +} upb_pb_encoder_segment; + +struct upb_pb_encoder { + upb_env *env; + + /* Our input and output. */ + upb_sink input_; + upb_bytessink *output_; + + /* The "subclosure" -- used as the inner closure as part of the bytessink + * protocol. */ + void *subc; + + /* The output buffer and limit, and our current write position. "buf" + * initially points to "initbuf", but is dynamically allocated if we need to + * grow beyond the initial size. */ + char *buf, *ptr, *limit; + + /* The beginning of the current run, or undefined if we are at the top + * level. */ + char *runbegin; + + /* The list of segments we are accumulating. */ + upb_pb_encoder_segment *segbuf, *segptr, *seglimit; + + /* The stack of enclosing submessages. Each entry in the stack points to the + * segment where this submessage's length is being accumulated. */ + int *stack, *top, *stacklimit; + + /* Depth of startmsg/endmsg calls. */ + int depth; +}; + +/* low-level buffering ********************************************************/ + +/* Low-level functions for interacting with the output buffer. */ + +/* TODO(haberman): handle pushback */ +static void putbuf(upb_pb_encoder *e, const char *buf, size_t len) { + size_t n = upb_bytessink_putbuf(e->output_, e->subc, buf, len, NULL); + UPB_ASSERT_VAR(n, n == len); +} + +static upb_pb_encoder_segment *top(upb_pb_encoder *e) { + return &e->segbuf[*e->top]; +} + +/* Call to ensure that at least "bytes" bytes are available for writing at + * e->ptr. Returns false if the bytes could not be allocated. */ +static bool reserve(upb_pb_encoder *e, size_t bytes) { + if ((size_t)(e->limit - e->ptr) < bytes) { + /* Grow buffer. */ + char *new_buf; + size_t needed = bytes + (e->ptr - e->buf); + size_t old_size = e->limit - e->buf; + + size_t new_size = old_size; + + while (new_size < needed) { + new_size *= 2; + } + + new_buf = upb_env_realloc(e->env, e->buf, old_size, new_size); + + if (new_buf == NULL) { + return false; + } + + e->ptr = new_buf + (e->ptr - e->buf); + e->runbegin = new_buf + (e->runbegin - e->buf); + e->limit = new_buf + new_size; + e->buf = new_buf; + } + + return true; +} + +/* Call when "bytes" bytes have been writte at e->ptr. The caller *must* have + * previously called reserve() with at least this many bytes. */ +static void encoder_advance(upb_pb_encoder *e, size_t bytes) { + assert((size_t)(e->limit - e->ptr) >= bytes); + e->ptr += bytes; +} + +/* Call when all of the bytes for a handler have been written. Flushes the + * bytes if possible and necessary, returning false if this failed. */ +static bool commit(upb_pb_encoder *e) { + if (!e->top) { + /* We aren't inside a delimited region. Flush our accumulated bytes to + * the output. + * + * TODO(haberman): in the future we may want to delay flushing for + * efficiency reasons. */ + putbuf(e, e->buf, e->ptr - e->buf); + e->ptr = e->buf; + } + + return true; +} + +/* Writes the given bytes to the buffer, handling reserve/advance. */ +static bool encode_bytes(upb_pb_encoder *e, const void *data, size_t len) { + if (!reserve(e, len)) { + return false; + } + + memcpy(e->ptr, data, len); + encoder_advance(e, len); + return true; +} + +/* Finish the current run by adding the run totals to the segment and message + * length. */ +static void accumulate(upb_pb_encoder *e) { + size_t run_len; + assert(e->ptr >= e->runbegin); + run_len = e->ptr - e->runbegin; + e->segptr->seglen += run_len; + top(e)->msglen += run_len; + e->runbegin = e->ptr; +} + +/* Call to indicate the start of delimited region for which the full length is + * not yet known. All data will be buffered until the length is known. + * Delimited regions may be nested; their lengths will all be tracked properly. */ +static bool start_delim(upb_pb_encoder *e) { + if (e->top) { + /* We are already buffering, advance to the next segment and push it on the + * stack. */ + accumulate(e); + + if (++e->top == e->stacklimit) { + /* TODO(haberman): grow stack? */ + return false; + } + + if (++e->segptr == e->seglimit) { + /* Grow segment buffer. */ + size_t old_size = + (e->seglimit - e->segbuf) * sizeof(upb_pb_encoder_segment); + size_t new_size = old_size * 2; + upb_pb_encoder_segment *new_buf = + upb_env_realloc(e->env, e->segbuf, old_size, new_size); + + if (new_buf == NULL) { + return false; + } + + e->segptr = new_buf + (e->segptr - e->segbuf); + e->seglimit = new_buf + (new_size / sizeof(upb_pb_encoder_segment)); + e->segbuf = new_buf; + } + } else { + /* We were previously at the top level, start buffering. */ + e->segptr = e->segbuf; + e->top = e->stack; + e->runbegin = e->ptr; + } + + *e->top = e->segptr - e->segbuf; + e->segptr->seglen = 0; + e->segptr->msglen = 0; + + return true; +} + +/* Call to indicate the end of a delimited region. We now know the length of + * the delimited region. If we are not nested inside any other delimited + * regions, we can now emit all of the buffered data we accumulated. */ +static bool end_delim(upb_pb_encoder *e) { + size_t msglen; + accumulate(e); + msglen = top(e)->msglen; + + if (e->top == e->stack) { + /* All lengths are now available, emit all buffered data. */ + char buf[UPB_PB_VARINT_MAX_LEN]; + upb_pb_encoder_segment *s; + const char *ptr = e->buf; + for (s = e->segbuf; s <= e->segptr; s++) { + size_t lenbytes = upb_vencode64(s->msglen, buf); + putbuf(e, buf, lenbytes); + putbuf(e, ptr, s->seglen); + ptr += s->seglen; + } + + e->ptr = e->buf; + e->top = NULL; + } else { + /* Need to keep buffering; propagate length info into enclosing + * submessages. */ + --e->top; + top(e)->msglen += msglen + upb_varint_size(msglen); + } + + return true; +} + + +/* tag_t **********************************************************************/ + +/* A precomputed (pre-encoded) tag and length. */ + +typedef struct { + uint8_t bytes; + char tag[7]; +} tag_t; + +/* Allocates a new tag for this field, and sets it in these handlerattr. */ +static void new_tag(upb_handlers *h, const upb_fielddef *f, upb_wiretype_t wt, + upb_handlerattr *attr) { + uint32_t n = upb_fielddef_number(f); + + tag_t *tag = malloc(sizeof(tag_t)); + tag->bytes = upb_vencode64((n << 3) | wt, tag->tag); + + upb_handlerattr_init(attr); + upb_handlerattr_sethandlerdata(attr, tag); + upb_handlers_addcleanup(h, tag, free); +} + +static bool encode_tag(upb_pb_encoder *e, const tag_t *tag) { + return encode_bytes(e, tag->tag, tag->bytes); +} + + +/* encoding of wire types *****************************************************/ + +static bool encode_fixed64(upb_pb_encoder *e, uint64_t val) { + /* TODO(haberman): byte-swap for big endian. */ + return encode_bytes(e, &val, sizeof(uint64_t)); +} + +static bool encode_fixed32(upb_pb_encoder *e, uint32_t val) { + /* TODO(haberman): byte-swap for big endian. */ + return encode_bytes(e, &val, sizeof(uint32_t)); +} + +static bool encode_varint(upb_pb_encoder *e, uint64_t val) { + if (!reserve(e, UPB_PB_VARINT_MAX_LEN)) { + return false; + } + + encoder_advance(e, upb_vencode64(val, e->ptr)); + return true; +} + +static uint64_t dbl2uint64(double d) { + uint64_t ret; + memcpy(&ret, &d, sizeof(uint64_t)); + return ret; +} + +static uint32_t flt2uint32(float d) { + uint32_t ret; + memcpy(&ret, &d, sizeof(uint32_t)); + return ret; +} + + +/* encoding of proto types ****************************************************/ + +static bool startmsg(void *c, const void *hd) { + upb_pb_encoder *e = c; + UPB_UNUSED(hd); + if (e->depth++ == 0) { + upb_bytessink_start(e->output_, 0, &e->subc); + } + return true; +} + +static bool endmsg(void *c, const void *hd, upb_status *status) { + upb_pb_encoder *e = c; + UPB_UNUSED(hd); + UPB_UNUSED(status); + if (--e->depth == 0) { + upb_bytessink_end(e->output_); + } + return true; +} + +static void *encode_startdelimfield(void *c, const void *hd) { + bool ok = encode_tag(c, hd) && commit(c) && start_delim(c); + return ok ? c : UPB_BREAK; +} + +static bool encode_enddelimfield(void *c, const void *hd) { + UPB_UNUSED(hd); + return end_delim(c); +} + +static void *encode_startgroup(void *c, const void *hd) { + return (encode_tag(c, hd) && commit(c)) ? c : UPB_BREAK; +} + +static bool encode_endgroup(void *c, const void *hd) { + return encode_tag(c, hd) && commit(c); +} + +static void *encode_startstr(void *c, const void *hd, size_t size_hint) { + UPB_UNUSED(size_hint); + return encode_startdelimfield(c, hd); +} + +static size_t encode_strbuf(void *c, const void *hd, const char *buf, + size_t len, const upb_bufhandle *h) { + UPB_UNUSED(hd); + UPB_UNUSED(h); + return encode_bytes(c, buf, len) ? len : 0; +} + +#define T(type, ctype, convert, encode) \ + static bool encode_scalar_##type(void *e, const void *hd, ctype val) { \ + return encode_tag(e, hd) && encode(e, (convert)(val)) && commit(e); \ + } \ + static bool encode_packed_##type(void *e, const void *hd, ctype val) { \ + UPB_UNUSED(hd); \ + return encode(e, (convert)(val)); \ + } + +T(double, double, dbl2uint64, encode_fixed64) +T(float, float, flt2uint32, encode_fixed32) +T(int64, int64_t, uint64_t, encode_varint) +T(int32, int32_t, uint32_t, encode_varint) +T(fixed64, uint64_t, uint64_t, encode_fixed64) +T(fixed32, uint32_t, uint32_t, encode_fixed32) +T(bool, bool, bool, encode_varint) +T(uint32, uint32_t, uint32_t, encode_varint) +T(uint64, uint64_t, uint64_t, encode_varint) +T(enum, int32_t, uint32_t, encode_varint) +T(sfixed32, int32_t, uint32_t, encode_fixed32) +T(sfixed64, int64_t, uint64_t, encode_fixed64) +T(sint32, int32_t, upb_zzenc_32, encode_varint) +T(sint64, int64_t, upb_zzenc_64, encode_varint) + +#undef T + + +/* code to build the handlers *************************************************/ + +static void newhandlers_callback(const void *closure, upb_handlers *h) { + const upb_msgdef *m; + upb_msg_field_iter i; + + UPB_UNUSED(closure); + + upb_handlers_setstartmsg(h, startmsg, NULL); + upb_handlers_setendmsg(h, endmsg, NULL); + + m = upb_handlers_msgdef(h); + for(upb_msg_field_begin(&i, m); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { + const upb_fielddef *f = upb_msg_iter_field(&i); + bool packed = upb_fielddef_isseq(f) && upb_fielddef_isprimitive(f) && + upb_fielddef_packed(f); + upb_handlerattr attr; + upb_wiretype_t wt = + packed ? UPB_WIRE_TYPE_DELIMITED + : upb_pb_native_wire_types[upb_fielddef_descriptortype(f)]; + + /* Pre-encode the tag for this field. */ + new_tag(h, f, wt, &attr); + + if (packed) { + upb_handlers_setstartseq(h, f, encode_startdelimfield, &attr); + upb_handlers_setendseq(h, f, encode_enddelimfield, &attr); + } + +#define T(upper, lower, upbtype) \ + case UPB_DESCRIPTOR_TYPE_##upper: \ + if (packed) { \ + upb_handlers_set##upbtype(h, f, encode_packed_##lower, &attr); \ + } else { \ + upb_handlers_set##upbtype(h, f, encode_scalar_##lower, &attr); \ + } \ + break; + + switch (upb_fielddef_descriptortype(f)) { + T(DOUBLE, double, double); + T(FLOAT, float, float); + T(INT64, int64, int64); + T(INT32, int32, int32); + T(FIXED64, fixed64, uint64); + T(FIXED32, fixed32, uint32); + T(BOOL, bool, bool); + T(UINT32, uint32, uint32); + T(UINT64, uint64, uint64); + T(ENUM, enum, int32); + T(SFIXED32, sfixed32, int32); + T(SFIXED64, sfixed64, int64); + T(SINT32, sint32, int32); + T(SINT64, sint64, int64); + case UPB_DESCRIPTOR_TYPE_STRING: + case UPB_DESCRIPTOR_TYPE_BYTES: + upb_handlers_setstartstr(h, f, encode_startstr, &attr); + upb_handlers_setendstr(h, f, encode_enddelimfield, &attr); + upb_handlers_setstring(h, f, encode_strbuf, &attr); + break; + case UPB_DESCRIPTOR_TYPE_MESSAGE: + upb_handlers_setstartsubmsg(h, f, encode_startdelimfield, &attr); + upb_handlers_setendsubmsg(h, f, encode_enddelimfield, &attr); + break; + case UPB_DESCRIPTOR_TYPE_GROUP: { + /* Endgroup takes a different tag (wire_type = END_GROUP). */ + upb_handlerattr attr2; + new_tag(h, f, UPB_WIRE_TYPE_END_GROUP, &attr2); + + upb_handlers_setstartsubmsg(h, f, encode_startgroup, &attr); + upb_handlers_setendsubmsg(h, f, encode_endgroup, &attr2); + + upb_handlerattr_uninit(&attr2); + break; + } + } + +#undef T + + upb_handlerattr_uninit(&attr); + } +} + +void upb_pb_encoder_reset(upb_pb_encoder *e) { + e->segptr = NULL; + e->top = NULL; + e->depth = 0; +} + + +/* public API *****************************************************************/ + +const upb_handlers *upb_pb_encoder_newhandlers(const upb_msgdef *m, + const void *owner) { + return upb_handlers_newfrozen(m, owner, newhandlers_callback, NULL); +} + +upb_pb_encoder *upb_pb_encoder_create(upb_env *env, const upb_handlers *h, + upb_bytessink *output) { + const size_t initial_bufsize = 256; + const size_t initial_segbufsize = 16; + /* TODO(haberman): make this configurable. */ + const size_t stack_size = 64; +#ifndef NDEBUG + const size_t size_before = upb_env_bytesallocated(env); +#endif + + upb_pb_encoder *e = upb_env_malloc(env, sizeof(upb_pb_encoder)); + if (!e) return NULL; + + e->buf = upb_env_malloc(env, initial_bufsize); + e->segbuf = upb_env_malloc(env, initial_segbufsize * sizeof(*e->segbuf)); + e->stack = upb_env_malloc(env, stack_size * sizeof(*e->stack)); + + if (!e->buf || !e->segbuf || !e->stack) { + return NULL; + } + + e->limit = e->buf + initial_bufsize; + e->seglimit = e->segbuf + initial_segbufsize; + e->stacklimit = e->stack + stack_size; + + upb_pb_encoder_reset(e); + upb_sink_reset(&e->input_, h, e); + + e->env = env; + e->output_ = output; + e->subc = output->closure; + e->ptr = e->buf; + + /* If this fails, increase the value in encoder.h. */ + assert(upb_env_bytesallocated(env) - size_before <= UPB_PB_ENCODER_SIZE); + return e; +} + +upb_sink *upb_pb_encoder_input(upb_pb_encoder *e) { return &e->input_; } + + +#include +#include +#include + +upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n, + void *owner, upb_status *status) { + /* Create handlers. */ + const upb_pbdecodermethod *decoder_m; + const upb_handlers *reader_h = upb_descreader_newhandlers(&reader_h); + upb_env env; + upb_pbdecodermethodopts opts; + upb_pbdecoder *decoder; + upb_descreader *reader; + bool ok; + upb_def **ret = NULL; + upb_def **defs; + + upb_pbdecodermethodopts_init(&opts, reader_h); + decoder_m = upb_pbdecodermethod_new(&opts, &decoder_m); + + upb_env_init(&env); + upb_env_reporterrorsto(&env, status); + + reader = upb_descreader_create(&env, reader_h); + decoder = upb_pbdecoder_create(&env, decoder_m, upb_descreader_input(reader)); + + /* Push input data. */ + ok = upb_bufsrc_putbuf(str, len, upb_pbdecoder_input(decoder)); + + if (!ok) goto cleanup; + defs = upb_descreader_getdefs(reader, owner, n); + ret = malloc(sizeof(upb_def*) * (*n)); + memcpy(ret, defs, sizeof(upb_def*) * (*n)); + +cleanup: + upb_env_uninit(&env); + upb_handlers_unref(reader_h, &reader_h); + upb_pbdecodermethod_unref(decoder_m, &decoder_m); + return ret; +} + +bool upb_load_descriptor_into_symtab(upb_symtab *s, const char *str, size_t len, + upb_status *status) { + int n; + bool success; + upb_def **defs = upb_load_defs_from_descriptor(str, len, &n, &defs, status); + if (!defs) return false; + success = upb_symtab_add(s, defs, n, &defs, status); + free(defs); + return success; +} + +char *upb_readfile(const char *filename, size_t *len) { + long size; + char *buf; + FILE *f = fopen(filename, "rb"); + if(!f) return NULL; + if(fseek(f, 0, SEEK_END) != 0) goto error; + size = ftell(f); + if(size < 0) goto error; + if(fseek(f, 0, SEEK_SET) != 0) goto error; + buf = malloc(size + 1); + if(size && fread(buf, size, 1, f) != 1) goto error; + fclose(f); + if (len) *len = size; + return buf; + +error: + fclose(f); + return NULL; +} + +bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname, + upb_status *status) { + size_t len; + bool success; + char *data = upb_readfile(fname, &len); + if (!data) { + if (status) upb_status_seterrf(status, "Couldn't read file: %s", fname); + return false; + } + success = upb_load_descriptor_into_symtab(symtab, data, len, status); + free(data); + return success; +} +/* + * upb::pb::TextPrinter + * + * OPT: This is not optimized at all. It uses printf() which parses the format + * string every time, and it allocates memory for every put. + */ + + +#include +#include +#include +#include +#include +#include +#include + + +struct upb_textprinter { + upb_sink input_; + upb_bytessink *output_; + int indent_depth_; + bool single_line_; + void *subc; +}; + +#define CHECK(x) if ((x) < 0) goto err; + +static const char *shortname(const char *longname) { + const char *last = strrchr(longname, '.'); + return last ? last + 1 : longname; +} + +static int indent(upb_textprinter *p) { + int i; + if (!p->single_line_) + for (i = 0; i < p->indent_depth_; i++) + upb_bytessink_putbuf(p->output_, p->subc, " ", 2, NULL); + return 0; +} + +static int endfield(upb_textprinter *p) { + const char ch = (p->single_line_ ? ' ' : '\n'); + upb_bytessink_putbuf(p->output_, p->subc, &ch, 1, NULL); + return 0; +} + +static int putescaped(upb_textprinter *p, const char *buf, size_t len, + bool preserve_utf8) { + /* Based on CEscapeInternal() from Google's protobuf release. */ + char dstbuf[4096], *dst = dstbuf, *dstend = dstbuf + sizeof(dstbuf); + const char *end = buf + len; + + /* I think hex is prettier and more useful, but proto2 uses octal; should + * investigate whether it can parse hex also. */ + const bool use_hex = false; + bool last_hex_escape = false; /* true if last output char was \xNN */ + + for (; buf < end; buf++) { + bool is_hex_escape; + + if (dstend - dst < 4) { + upb_bytessink_putbuf(p->output_, p->subc, dstbuf, dst - dstbuf, NULL); + dst = dstbuf; + } + + is_hex_escape = false; + switch (*buf) { + case '\n': *(dst++) = '\\'; *(dst++) = 'n'; break; + case '\r': *(dst++) = '\\'; *(dst++) = 'r'; break; + case '\t': *(dst++) = '\\'; *(dst++) = 't'; break; + case '\"': *(dst++) = '\\'; *(dst++) = '\"'; break; + case '\'': *(dst++) = '\\'; *(dst++) = '\''; break; + case '\\': *(dst++) = '\\'; *(dst++) = '\\'; break; + default: + /* Note that if we emit \xNN and the buf character after that is a hex + * digit then that digit must be escaped too to prevent it being + * interpreted as part of the character code by C. */ + if ((!preserve_utf8 || (uint8_t)*buf < 0x80) && + (!isprint(*buf) || (last_hex_escape && isxdigit(*buf)))) { + sprintf(dst, (use_hex ? "\\x%02x" : "\\%03o"), (uint8_t)*buf); + is_hex_escape = use_hex; + dst += 4; + } else { + *(dst++) = *buf; break; + } + } + last_hex_escape = is_hex_escape; + } + /* Flush remaining data. */ + upb_bytessink_putbuf(p->output_, p->subc, dstbuf, dst - dstbuf, NULL); + return 0; +} + +bool putf(upb_textprinter *p, const char *fmt, ...) { + va_list args; + va_list args_copy; + char *str; + int written; + int len; + bool ok; + + va_start(args, fmt); + + /* Run once to get the length of the string. */ + _upb_va_copy(args_copy, args); + len = _upb_vsnprintf(NULL, 0, fmt, args_copy); + va_end(args_copy); + + /* + 1 for NULL terminator (vsprintf() requires it even if we don't). */ + str = malloc(len + 1); + if (!str) return false; + written = vsprintf(str, fmt, args); + va_end(args); + UPB_ASSERT_VAR(written, written == len); + + ok = upb_bytessink_putbuf(p->output_, p->subc, str, len, NULL); + free(str); + return ok; +} + + +/* handlers *******************************************************************/ + +static bool textprinter_startmsg(void *c, const void *hd) { + upb_textprinter *p = c; + UPB_UNUSED(hd); + if (p->indent_depth_ == 0) { + upb_bytessink_start(p->output_, 0, &p->subc); + } + return true; +} + +static bool textprinter_endmsg(void *c, const void *hd, upb_status *s) { + upb_textprinter *p = c; + UPB_UNUSED(hd); + UPB_UNUSED(s); + if (p->indent_depth_ == 0) { + upb_bytessink_end(p->output_); + } + return true; +} + +#define TYPE(name, ctype, fmt) \ + static bool textprinter_put ## name(void *closure, const void *handler_data, \ + ctype val) { \ + upb_textprinter *p = closure; \ + const upb_fielddef *f = handler_data; \ + CHECK(indent(p)); \ + putf(p, "%s: " fmt, upb_fielddef_name(f), val); \ + CHECK(endfield(p)); \ + return true; \ + err: \ + return false; \ +} + +static bool textprinter_putbool(void *closure, const void *handler_data, + bool val) { + upb_textprinter *p = closure; + const upb_fielddef *f = handler_data; + CHECK(indent(p)); + putf(p, "%s: %s", upb_fielddef_name(f), val ? "true" : "false"); + CHECK(endfield(p)); + return true; +err: + return false; +} + +#define STRINGIFY_HELPER(x) #x +#define STRINGIFY_MACROVAL(x) STRINGIFY_HELPER(x) + +TYPE(int32, int32_t, "%" PRId32) +TYPE(int64, int64_t, "%" PRId64) +TYPE(uint32, uint32_t, "%" PRIu32) +TYPE(uint64, uint64_t, "%" PRIu64) +TYPE(float, float, "%." STRINGIFY_MACROVAL(FLT_DIG) "g") +TYPE(double, double, "%." STRINGIFY_MACROVAL(DBL_DIG) "g") + +#undef TYPE + +/* Output a symbolic value from the enum if found, else just print as int32. */ +static bool textprinter_putenum(void *closure, const void *handler_data, + int32_t val) { + upb_textprinter *p = closure; + const upb_fielddef *f = handler_data; + const upb_enumdef *enum_def = upb_downcast_enumdef(upb_fielddef_subdef(f)); + const char *label = upb_enumdef_iton(enum_def, val); + if (label) { + indent(p); + putf(p, "%s: %s", upb_fielddef_name(f), label); + endfield(p); + } else { + if (!textprinter_putint32(closure, handler_data, val)) + return false; + } + return true; +} + +static void *textprinter_startstr(void *closure, const void *handler_data, + size_t size_hint) { + upb_textprinter *p = closure; + const upb_fielddef *f = handler_data; + UPB_UNUSED(size_hint); + indent(p); + putf(p, "%s: \"", upb_fielddef_name(f)); + return p; +} + +static bool textprinter_endstr(void *closure, const void *handler_data) { + upb_textprinter *p = closure; + UPB_UNUSED(handler_data); + putf(p, "\""); + endfield(p); + return true; +} + +static size_t textprinter_putstr(void *closure, const void *hd, const char *buf, + size_t len, const upb_bufhandle *handle) { + upb_textprinter *p = closure; + const upb_fielddef *f = hd; + UPB_UNUSED(handle); + CHECK(putescaped(p, buf, len, upb_fielddef_type(f) == UPB_TYPE_STRING)); + return len; +err: + return 0; +} + +static void *textprinter_startsubmsg(void *closure, const void *handler_data) { + upb_textprinter *p = closure; + const char *name = handler_data; + CHECK(indent(p)); + putf(p, "%s {%c", name, p->single_line_ ? ' ' : '\n'); + p->indent_depth_++; + return p; +err: + return UPB_BREAK; +} + +static bool textprinter_endsubmsg(void *closure, const void *handler_data) { + upb_textprinter *p = closure; + UPB_UNUSED(handler_data); + p->indent_depth_--; + CHECK(indent(p)); + upb_bytessink_putbuf(p->output_, p->subc, "}", 1, NULL); + CHECK(endfield(p)); + return true; +err: + return false; +} + +static void onmreg(const void *c, upb_handlers *h) { + const upb_msgdef *m = upb_handlers_msgdef(h); + upb_msg_field_iter i; + UPB_UNUSED(c); + + upb_handlers_setstartmsg(h, textprinter_startmsg, NULL); + upb_handlers_setendmsg(h, textprinter_endmsg, NULL); + + for(upb_msg_field_begin(&i, m); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { + upb_fielddef *f = upb_msg_iter_field(&i); + upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; + upb_handlerattr_sethandlerdata(&attr, f); + switch (upb_fielddef_type(f)) { + case UPB_TYPE_INT32: + upb_handlers_setint32(h, f, textprinter_putint32, &attr); + break; + case UPB_TYPE_INT64: + upb_handlers_setint64(h, f, textprinter_putint64, &attr); + break; + case UPB_TYPE_UINT32: + upb_handlers_setuint32(h, f, textprinter_putuint32, &attr); + break; + case UPB_TYPE_UINT64: + upb_handlers_setuint64(h, f, textprinter_putuint64, &attr); + break; + case UPB_TYPE_FLOAT: + upb_handlers_setfloat(h, f, textprinter_putfloat, &attr); + break; + case UPB_TYPE_DOUBLE: + upb_handlers_setdouble(h, f, textprinter_putdouble, &attr); + break; + case UPB_TYPE_BOOL: + upb_handlers_setbool(h, f, textprinter_putbool, &attr); + break; + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: + upb_handlers_setstartstr(h, f, textprinter_startstr, &attr); + upb_handlers_setstring(h, f, textprinter_putstr, &attr); + upb_handlers_setendstr(h, f, textprinter_endstr, &attr); + break; + case UPB_TYPE_MESSAGE: { + const char *name = + upb_fielddef_istagdelim(f) + ? shortname(upb_msgdef_fullname(upb_fielddef_msgsubdef(f))) + : upb_fielddef_name(f); + upb_handlerattr_sethandlerdata(&attr, name); + upb_handlers_setstartsubmsg(h, f, textprinter_startsubmsg, &attr); + upb_handlers_setendsubmsg(h, f, textprinter_endsubmsg, &attr); + break; + } + case UPB_TYPE_ENUM: + upb_handlers_setint32(h, f, textprinter_putenum, &attr); + break; + } + } +} + +static void textprinter_reset(upb_textprinter *p, bool single_line) { + p->single_line_ = single_line; + p->indent_depth_ = 0; +} + + +/* Public API *****************************************************************/ + +upb_textprinter *upb_textprinter_create(upb_env *env, const upb_handlers *h, + upb_bytessink *output) { + upb_textprinter *p = upb_env_malloc(env, sizeof(upb_textprinter)); + if (!p) return NULL; + + p->output_ = output; + upb_sink_reset(&p->input_, h, p); + textprinter_reset(p, false); + + return p; +} + +const upb_handlers *upb_textprinter_newhandlers(const upb_msgdef *m, + const void *owner) { + return upb_handlers_newfrozen(m, owner, &onmreg, NULL); +} + +upb_sink *upb_textprinter_input(upb_textprinter *p) { return &p->input_; } + +void upb_textprinter_setsingleline(upb_textprinter *p, bool single_line) { + p->single_line_ = single_line; +} + + +/* Index is descriptor type. */ +const uint8_t upb_pb_native_wire_types[] = { + UPB_WIRE_TYPE_END_GROUP, /* ENDGROUP */ + UPB_WIRE_TYPE_64BIT, /* DOUBLE */ + UPB_WIRE_TYPE_32BIT, /* FLOAT */ + UPB_WIRE_TYPE_VARINT, /* INT64 */ + UPB_WIRE_TYPE_VARINT, /* UINT64 */ + UPB_WIRE_TYPE_VARINT, /* INT32 */ + UPB_WIRE_TYPE_64BIT, /* FIXED64 */ + UPB_WIRE_TYPE_32BIT, /* FIXED32 */ + UPB_WIRE_TYPE_VARINT, /* BOOL */ + UPB_WIRE_TYPE_DELIMITED, /* STRING */ + UPB_WIRE_TYPE_START_GROUP, /* GROUP */ + UPB_WIRE_TYPE_DELIMITED, /* MESSAGE */ + UPB_WIRE_TYPE_DELIMITED, /* BYTES */ + UPB_WIRE_TYPE_VARINT, /* UINT32 */ + UPB_WIRE_TYPE_VARINT, /* ENUM */ + UPB_WIRE_TYPE_32BIT, /* SFIXED32 */ + UPB_WIRE_TYPE_64BIT, /* SFIXED64 */ + UPB_WIRE_TYPE_VARINT, /* SINT32 */ + UPB_WIRE_TYPE_VARINT, /* SINT64 */ +}; + +/* A basic branch-based decoder, uses 32-bit values to get good performance + * on 32-bit architectures (but performs well on 64-bits also). + * This scheme comes from the original Google Protobuf implementation + * (proto2). */ +upb_decoderet upb_vdecode_max8_branch32(upb_decoderet r) { + upb_decoderet err = {NULL, 0}; + const char *p = r.p; + uint32_t low = (uint32_t)r.val; + uint32_t high = 0; + uint32_t b; + b = *(p++); low |= (b & 0x7fU) << 14; if (!(b & 0x80)) goto done; + b = *(p++); low |= (b & 0x7fU) << 21; if (!(b & 0x80)) goto done; + b = *(p++); low |= (b & 0x7fU) << 28; + high = (b & 0x7fU) >> 4; if (!(b & 0x80)) goto done; + b = *(p++); high |= (b & 0x7fU) << 3; if (!(b & 0x80)) goto done; + b = *(p++); high |= (b & 0x7fU) << 10; if (!(b & 0x80)) goto done; + b = *(p++); high |= (b & 0x7fU) << 17; if (!(b & 0x80)) goto done; + b = *(p++); high |= (b & 0x7fU) << 24; if (!(b & 0x80)) goto done; + b = *(p++); high |= (b & 0x7fU) << 31; if (!(b & 0x80)) goto done; + return err; + +done: + r.val = ((uint64_t)high << 32) | low; + r.p = p; + return r; +} + +/* Like the previous, but uses 64-bit values. */ +upb_decoderet upb_vdecode_max8_branch64(upb_decoderet r) { + const char *p = r.p; + uint64_t val = r.val; + uint64_t b; + upb_decoderet err = {NULL, 0}; + b = *(p++); val |= (b & 0x7fU) << 14; if (!(b & 0x80)) goto done; + b = *(p++); val |= (b & 0x7fU) << 21; if (!(b & 0x80)) goto done; + b = *(p++); val |= (b & 0x7fU) << 28; if (!(b & 0x80)) goto done; + b = *(p++); val |= (b & 0x7fU) << 35; if (!(b & 0x80)) goto done; + b = *(p++); val |= (b & 0x7fU) << 42; if (!(b & 0x80)) goto done; + b = *(p++); val |= (b & 0x7fU) << 49; if (!(b & 0x80)) goto done; + b = *(p++); val |= (b & 0x7fU) << 56; if (!(b & 0x80)) goto done; + b = *(p++); val |= (b & 0x7fU) << 63; if (!(b & 0x80)) goto done; + return err; + +done: + r.val = val; + r.p = p; + return r; +} + +/* Given an encoded varint v, returns an integer with a single bit set that + * indicates the end of the varint. Subtracting one from this value will + * yield a mask that leaves only bits that are part of the varint. Returns + * 0 if the varint is unterminated. */ +static uint64_t upb_get_vstopbit(uint64_t v) { + uint64_t cbits = v | 0x7f7f7f7f7f7f7f7fULL; + return ~cbits & (cbits+1); +} + +/* A branchless decoder. Credit to Pascal Massimino for the bit-twiddling. */ +upb_decoderet upb_vdecode_max8_massimino(upb_decoderet r) { + uint64_t b; + uint64_t stop_bit; + upb_decoderet my_r; + memcpy(&b, r.p, sizeof(b)); + stop_bit = upb_get_vstopbit(b); + b = (b & 0x7f7f7f7f7f7f7f7fULL) & (stop_bit - 1); + b += b & 0x007f007f007f007fULL; + b += 3 * (b & 0x0000ffff0000ffffULL); + b += 15 * (b & 0x00000000ffffffffULL); + if (stop_bit == 0) { + /* Error: unterminated varint. */ + upb_decoderet err_r = {(void*)0, 0}; + return err_r; + } + my_r = upb_decoderet_make(r.p + ((__builtin_ctzll(stop_bit) + 1) / 8), + r.val | (b << 7)); + return my_r; +} + +/* A branchless decoder. Credit to Daniel Wright for the bit-twiddling. */ +upb_decoderet upb_vdecode_max8_wright(upb_decoderet r) { + uint64_t b; + uint64_t stop_bit; + upb_decoderet my_r; + memcpy(&b, r.p, sizeof(b)); + stop_bit = upb_get_vstopbit(b); + b &= (stop_bit - 1); + b = ((b & 0x7f007f007f007f00ULL) >> 1) | (b & 0x007f007f007f007fULL); + b = ((b & 0xffff0000ffff0000ULL) >> 2) | (b & 0x0000ffff0000ffffULL); + b = ((b & 0xffffffff00000000ULL) >> 4) | (b & 0x00000000ffffffffULL); + if (stop_bit == 0) { + /* Error: unterminated varint. */ + upb_decoderet err_r = {(void*)0, 0}; + return err_r; + } + my_r = upb_decoderet_make(r.p + ((__builtin_ctzll(stop_bit) + 1) / 8), + r.val | (b << 14)); + return my_r; +} + +#line 1 "upb/json/parser.rl" +/* +** upb::json::Parser (upb_json_parser) +** +** A parser that uses the Ragel State Machine Compiler to generate +** the finite automata. +** +** Ragel only natively handles regular languages, but we can manually +** program it a bit to handle context-free languages like JSON, by using +** the "fcall" and "fret" constructs. +** +** This parser can handle the basics, but needs several things to be fleshed +** out: +** +** - handling of unicode escape sequences (including high surrogate pairs). +** - properly check and report errors for unknown fields, stack overflow, +** improper array nesting (or lack of nesting). +** - handling of base64 sequences with padding characters. +** - handling of push-back (non-success returns from sink functions). +** - handling of keys/escape-sequences/etc that span input buffers. +*/ + +#include +#include +#include +#include +#include +#include + + +#define UPB_JSON_MAX_DEPTH 64 + +typedef struct { + upb_sink sink; + + /* The current message in which we're parsing, and the field whose value we're + * expecting next. */ + const upb_msgdef *m; + const upb_fielddef *f; + + /* We are in a repeated-field context, ready to emit mapentries as + * submessages. This flag alters the start-of-object (open-brace) behavior to + * begin a sequence of mapentry messages rather than a single submessage. */ + bool is_map; + + /* We are in a map-entry message context. This flag is set when parsing the + * value field of a single map entry and indicates to all value-field parsers + * (subobjects, strings, numbers, and bools) that the map-entry submessage + * should end as soon as the value is parsed. */ + bool is_mapentry; + + /* If |is_map| or |is_mapentry| is true, |mapfield| refers to the parent + * message's map field that we're currently parsing. This differs from |f| + * because |f| is the field in the *current* message (i.e., the map-entry + * message itself), not the parent's field that leads to this map. */ + const upb_fielddef *mapfield; +} upb_jsonparser_frame; + +struct upb_json_parser { + upb_env *env; + upb_byteshandler input_handler_; + upb_bytessink input_; + + /* Stack to track the JSON scopes we are in. */ + upb_jsonparser_frame stack[UPB_JSON_MAX_DEPTH]; + upb_jsonparser_frame *top; + upb_jsonparser_frame *limit; + + upb_status status; + + /* Ragel's internal parsing stack for the parsing state machine. */ + int current_state; + int parser_stack[UPB_JSON_MAX_DEPTH]; + int parser_top; + + /* The handle for the current buffer. */ + const upb_bufhandle *handle; + + /* Accumulate buffer. See details in parser.rl. */ + const char *accumulated; + size_t accumulated_len; + char *accumulate_buf; + size_t accumulate_buf_size; + + /* Multi-part text data. See details in parser.rl. */ + int multipart_state; + upb_selector_t string_selector; + + /* Input capture. See details in parser.rl. */ + const char *capture; + + /* Intermediate result of parsing a unicode escape sequence. */ + uint32_t digit; +}; + +#define PARSER_CHECK_RETURN(x) if (!(x)) return false + +/* Used to signal that a capture has been suspended. */ +static char suspend_capture; + +static upb_selector_t getsel_for_handlertype(upb_json_parser *p, + upb_handlertype_t type) { + upb_selector_t sel; + bool ok = upb_handlers_getselector(p->top->f, type, &sel); + UPB_ASSERT_VAR(ok, ok); + return sel; +} + +static upb_selector_t parser_getsel(upb_json_parser *p) { + return getsel_for_handlertype( + p, upb_handlers_getprimitivehandlertype(p->top->f)); +} + +static bool check_stack(upb_json_parser *p) { + if ((p->top + 1) == p->limit) { + upb_status_seterrmsg(&p->status, "Nesting too deep"); + upb_env_reporterror(p->env, &p->status); + return false; + } + + return true; +} + +/* There are GCC/Clang built-ins for overflow checking which we could start + * using if there was any performance benefit to it. */ + +static bool checked_add(size_t a, size_t b, size_t *c) { + if (SIZE_MAX - a < b) return false; + *c = a + b; + return true; +} + +static size_t saturating_multiply(size_t a, size_t b) { + /* size_t is unsigned, so this is defined behavior even on overflow. */ + size_t ret = a * b; + if (b != 0 && ret / b != a) { + ret = SIZE_MAX; + } + return ret; +} + + +/* Base64 decoding ************************************************************/ + +/* TODO(haberman): make this streaming. */ + +static const signed char b64table[] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62/*+*/, -1, -1, -1, 63/*/ */, + 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/, + 60/*8*/, 61/*9*/, -1, -1, -1, -1, -1, -1, + -1, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/, + 07/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/, + 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/, + 23/*X*/, 24/*Y*/, 25/*Z*/, -1, -1, -1, -1, -1, + -1, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/, + 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/, + 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/, + 49/*x*/, 50/*y*/, 51/*z*/, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +/* Returns the table value sign-extended to 32 bits. Knowing that the upper + * bits will be 1 for unrecognized characters makes it easier to check for + * this error condition later (see below). */ +int32_t b64lookup(unsigned char ch) { return b64table[ch]; } + +/* Returns true if the given character is not a valid base64 character or + * padding. */ +bool nonbase64(unsigned char ch) { return b64lookup(ch) == -1 && ch != '='; } + +static bool base64_push(upb_json_parser *p, upb_selector_t sel, const char *ptr, + size_t len) { + const char *limit = ptr + len; + for (; ptr < limit; ptr += 4) { + uint32_t val; + char output[3]; + + if (limit - ptr < 4) { + upb_status_seterrf(&p->status, + "Base64 input for bytes field not a multiple of 4: %s", + upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); + return false; + } + + val = b64lookup(ptr[0]) << 18 | + b64lookup(ptr[1]) << 12 | + b64lookup(ptr[2]) << 6 | + b64lookup(ptr[3]); + + /* Test the upper bit; returns true if any of the characters returned -1. */ + if (val & 0x80000000) { + goto otherchar; + } + + output[0] = val >> 16; + output[1] = (val >> 8) & 0xff; + output[2] = val & 0xff; + upb_sink_putstring(&p->top->sink, sel, output, 3, NULL); + } + return true; + +otherchar: + if (nonbase64(ptr[0]) || nonbase64(ptr[1]) || nonbase64(ptr[2]) || + nonbase64(ptr[3]) ) { + upb_status_seterrf(&p->status, + "Non-base64 characters in bytes field: %s", + upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); + return false; + } if (ptr[2] == '=') { + uint32_t val; + char output; + + /* Last group contains only two input bytes, one output byte. */ + if (ptr[0] == '=' || ptr[1] == '=' || ptr[3] != '=') { + goto badpadding; + } + + val = b64lookup(ptr[0]) << 18 | + b64lookup(ptr[1]) << 12; + + assert(!(val & 0x80000000)); + output = val >> 16; + upb_sink_putstring(&p->top->sink, sel, &output, 1, NULL); + return true; + } else { + uint32_t val; + char output[2]; + + /* Last group contains only three input bytes, two output bytes. */ + if (ptr[0] == '=' || ptr[1] == '=' || ptr[2] == '=') { + goto badpadding; + } + + val = b64lookup(ptr[0]) << 18 | + b64lookup(ptr[1]) << 12 | + b64lookup(ptr[2]) << 6; + + output[0] = val >> 16; + output[1] = (val >> 8) & 0xff; + upb_sink_putstring(&p->top->sink, sel, output, 2, NULL); + return true; + } + +badpadding: + upb_status_seterrf(&p->status, + "Incorrect base64 padding for field: %s (%.*s)", + upb_fielddef_name(p->top->f), + 4, ptr); + upb_env_reporterror(p->env, &p->status); + return false; +} + + +/* Accumulate buffer **********************************************************/ + +/* Functionality for accumulating a buffer. + * + * Some parts of the parser need an entire value as a contiguous string. For + * example, to look up a member name in a hash table, or to turn a string into + * a number, the relevant library routines need the input string to be in + * contiguous memory, even if the value spanned two or more buffers in the + * input. These routines handle that. + * + * In the common case we can just point to the input buffer to get this + * contiguous string and avoid any actual copy. So we optimistically begin + * this way. But there are a few cases where we must instead copy into a + * separate buffer: + * + * 1. The string was not contiguous in the input (it spanned buffers). + * + * 2. The string included escape sequences that need to be interpreted to get + * the true value in a contiguous buffer. */ + +static void assert_accumulate_empty(upb_json_parser *p) { + UPB_UNUSED(p); + assert(p->accumulated == NULL); + assert(p->accumulated_len == 0); +} + +static void accumulate_clear(upb_json_parser *p) { + p->accumulated = NULL; + p->accumulated_len = 0; +} + +/* Used internally by accumulate_append(). */ +static bool accumulate_realloc(upb_json_parser *p, size_t need) { + void *mem; + size_t old_size = p->accumulate_buf_size; + size_t new_size = UPB_MAX(old_size, 128); + while (new_size < need) { + new_size = saturating_multiply(new_size, 2); + } + + mem = upb_env_realloc(p->env, p->accumulate_buf, old_size, new_size); + if (!mem) { + upb_status_seterrmsg(&p->status, "Out of memory allocating buffer."); + upb_env_reporterror(p->env, &p->status); + return false; + } + + p->accumulate_buf = mem; + p->accumulate_buf_size = new_size; + return true; +} + +/* Logically appends the given data to the append buffer. + * If "can_alias" is true, we will try to avoid actually copying, but the buffer + * must be valid until the next accumulate_append() call (if any). */ +static bool accumulate_append(upb_json_parser *p, const char *buf, size_t len, + bool can_alias) { + size_t need; + + if (!p->accumulated && can_alias) { + p->accumulated = buf; + p->accumulated_len = len; + return true; + } + + if (!checked_add(p->accumulated_len, len, &need)) { + upb_status_seterrmsg(&p->status, "Integer overflow."); + upb_env_reporterror(p->env, &p->status); + return false; + } + + if (need > p->accumulate_buf_size && !accumulate_realloc(p, need)) { + return false; + } + + if (p->accumulated != p->accumulate_buf) { + memcpy(p->accumulate_buf, p->accumulated, p->accumulated_len); + p->accumulated = p->accumulate_buf; + } + + memcpy(p->accumulate_buf + p->accumulated_len, buf, len); + p->accumulated_len += len; + return true; +} + +/* Returns a pointer to the data accumulated since the last accumulate_clear() + * call, and writes the length to *len. This with point either to the input + * buffer or a temporary accumulate buffer. */ +static const char *accumulate_getptr(upb_json_parser *p, size_t *len) { + assert(p->accumulated); + *len = p->accumulated_len; + return p->accumulated; +} + + +/* Mult-part text data ********************************************************/ + +/* When we have text data in the input, it can often come in multiple segments. + * For example, there may be some raw string data followed by an escape + * sequence. The two segments are processed with different logic. Also buffer + * seams in the input can cause multiple segments. + * + * As we see segments, there are two main cases for how we want to process them: + * + * 1. we want to push the captured input directly to string handlers. + * + * 2. we need to accumulate all the parts into a contiguous buffer for further + * processing (field name lookup, string->number conversion, etc). */ + +/* This is the set of states for p->multipart_state. */ +enum { + /* We are not currently processing multipart data. */ + MULTIPART_INACTIVE = 0, + + /* We are processing multipart data by accumulating it into a contiguous + * buffer. */ + MULTIPART_ACCUMULATE = 1, + + /* We are processing multipart data by pushing each part directly to the + * current string handlers. */ + MULTIPART_PUSHEAGERLY = 2 +}; + +/* Start a multi-part text value where we accumulate the data for processing at + * the end. */ +static void multipart_startaccum(upb_json_parser *p) { + assert_accumulate_empty(p); + assert(p->multipart_state == MULTIPART_INACTIVE); + p->multipart_state = MULTIPART_ACCUMULATE; +} + +/* Start a multi-part text value where we immediately push text data to a string + * value with the given selector. */ +static void multipart_start(upb_json_parser *p, upb_selector_t sel) { + assert_accumulate_empty(p); + assert(p->multipart_state == MULTIPART_INACTIVE); + p->multipart_state = MULTIPART_PUSHEAGERLY; + p->string_selector = sel; +} + +static bool multipart_text(upb_json_parser *p, const char *buf, size_t len, + bool can_alias) { + switch (p->multipart_state) { + case MULTIPART_INACTIVE: + upb_status_seterrmsg( + &p->status, "Internal error: unexpected state MULTIPART_INACTIVE"); + upb_env_reporterror(p->env, &p->status); + return false; + + case MULTIPART_ACCUMULATE: + if (!accumulate_append(p, buf, len, can_alias)) { + return false; + } + break; + + case MULTIPART_PUSHEAGERLY: { + const upb_bufhandle *handle = can_alias ? p->handle : NULL; + upb_sink_putstring(&p->top->sink, p->string_selector, buf, len, handle); + break; + } + } + + return true; +} + +/* Note: this invalidates the accumulate buffer! Call only after reading its + * contents. */ +static void multipart_end(upb_json_parser *p) { + assert(p->multipart_state != MULTIPART_INACTIVE); + p->multipart_state = MULTIPART_INACTIVE; + accumulate_clear(p); +} + + +/* Input capture **************************************************************/ + +/* Functionality for capturing a region of the input as text. Gracefully + * handles the case where a buffer seam occurs in the middle of the captured + * region. */ + +static void capture_begin(upb_json_parser *p, const char *ptr) { + assert(p->multipart_state != MULTIPART_INACTIVE); + assert(p->capture == NULL); + p->capture = ptr; +} + +static bool capture_end(upb_json_parser *p, const char *ptr) { + assert(p->capture); + if (multipart_text(p, p->capture, ptr - p->capture, true)) { + p->capture = NULL; + return true; + } else { + return false; + } +} + +/* This is called at the end of each input buffer (ie. when we have hit a + * buffer seam). If we are in the middle of capturing the input, this + * processes the unprocessed capture region. */ +static void capture_suspend(upb_json_parser *p, const char **ptr) { + if (!p->capture) return; + + if (multipart_text(p, p->capture, *ptr - p->capture, false)) { + /* We use this as a signal that we were in the middle of capturing, and + * that capturing should resume at the beginning of the next buffer. + * + * We can't use *ptr here, because we have no guarantee that this pointer + * will be valid when we resume (if the underlying memory is freed, then + * using the pointer at all, even to compare to NULL, is likely undefined + * behavior). */ + p->capture = &suspend_capture; + } else { + /* Need to back up the pointer to the beginning of the capture, since + * we were not able to actually preserve it. */ + *ptr = p->capture; + } +} + +static void capture_resume(upb_json_parser *p, const char *ptr) { + if (p->capture) { + assert(p->capture == &suspend_capture); + p->capture = ptr; + } +} + + +/* Callbacks from the parser **************************************************/ + +/* These are the functions called directly from the parser itself. + * We define these in the same order as their declarations in the parser. */ + +static char escape_char(char in) { + switch (in) { + case 'r': return '\r'; + case 't': return '\t'; + case 'n': return '\n'; + case 'f': return '\f'; + case 'b': return '\b'; + case '/': return '/'; + case '"': return '"'; + case '\\': return '\\'; + default: + assert(0); + return 'x'; + } +} + +static bool escape(upb_json_parser *p, const char *ptr) { + char ch = escape_char(*ptr); + return multipart_text(p, &ch, 1, false); +} + +static void start_hex(upb_json_parser *p) { + p->digit = 0; +} + +static void hexdigit(upb_json_parser *p, const char *ptr) { + char ch = *ptr; + + p->digit <<= 4; + + if (ch >= '0' && ch <= '9') { + p->digit += (ch - '0'); + } else if (ch >= 'a' && ch <= 'f') { + p->digit += ((ch - 'a') + 10); + } else { + assert(ch >= 'A' && ch <= 'F'); + p->digit += ((ch - 'A') + 10); + } +} + +static bool end_hex(upb_json_parser *p) { + uint32_t codepoint = p->digit; + + /* emit the codepoint as UTF-8. */ + char utf8[3]; /* support \u0000 -- \uFFFF -- need only three bytes. */ + int length = 0; + if (codepoint <= 0x7F) { + utf8[0] = codepoint; + length = 1; + } else if (codepoint <= 0x07FF) { + utf8[1] = (codepoint & 0x3F) | 0x80; + codepoint >>= 6; + utf8[0] = (codepoint & 0x1F) | 0xC0; + length = 2; + } else /* codepoint <= 0xFFFF */ { + utf8[2] = (codepoint & 0x3F) | 0x80; + codepoint >>= 6; + utf8[1] = (codepoint & 0x3F) | 0x80; + codepoint >>= 6; + utf8[0] = (codepoint & 0x0F) | 0xE0; + length = 3; + } + /* TODO(haberman): Handle high surrogates: if codepoint is a high surrogate + * we have to wait for the next escape to get the full code point). */ + + return multipart_text(p, utf8, length, false); +} + +static void start_text(upb_json_parser *p, const char *ptr) { + capture_begin(p, ptr); +} + +static bool end_text(upb_json_parser *p, const char *ptr) { + return capture_end(p, ptr); +} + +static void start_number(upb_json_parser *p, const char *ptr) { + multipart_startaccum(p); + capture_begin(p, ptr); +} + +static bool parse_number(upb_json_parser *p); + +static bool end_number(upb_json_parser *p, const char *ptr) { + if (!capture_end(p, ptr)) { + return false; + } + + return parse_number(p); +} + +static bool parse_number(upb_json_parser *p) { + size_t len; + const char *buf; + const char *myend; + char *end; + + /* strtol() and friends unfortunately do not support specifying the length of + * the input string, so we need to force a copy into a NULL-terminated buffer. */ + if (!multipart_text(p, "\0", 1, false)) { + return false; + } + + buf = accumulate_getptr(p, &len); + myend = buf + len - 1; /* One for NULL. */ + + /* XXX: We are using strtol to parse integers, but this is wrong as even + * integers can be represented as 1e6 (for example), which strtol can't + * handle correctly. + * + * XXX: Also, we can't handle large integers properly because strto[u]ll + * isn't in C89. + * + * XXX: Also, we don't properly check floats for overflow, since strtof + * isn't in C89. */ + switch (upb_fielddef_type(p->top->f)) { + case UPB_TYPE_ENUM: + case UPB_TYPE_INT32: { + long val = strtol(p->accumulated, &end, 0); + if (val > INT32_MAX || val < INT32_MIN || errno == ERANGE || end != myend) + goto err; + else + upb_sink_putint32(&p->top->sink, parser_getsel(p), val); + break; + } + case UPB_TYPE_INT64: { + long long val = strtol(p->accumulated, &end, 0); + if (val > INT64_MAX || val < INT64_MIN || errno == ERANGE || end != myend) + goto err; + else + upb_sink_putint64(&p->top->sink, parser_getsel(p), val); + break; + } + case UPB_TYPE_UINT32: { + unsigned long val = strtoul(p->accumulated, &end, 0); + if (val > UINT32_MAX || errno == ERANGE || end != myend) + goto err; + else + upb_sink_putuint32(&p->top->sink, parser_getsel(p), val); + break; + } + case UPB_TYPE_UINT64: { + unsigned long long val = strtoul(p->accumulated, &end, 0); + if (val > UINT64_MAX || errno == ERANGE || end != myend) + goto err; + else + upb_sink_putuint64(&p->top->sink, parser_getsel(p), val); + break; + } + case UPB_TYPE_DOUBLE: { + double val = strtod(p->accumulated, &end); + if (errno == ERANGE || end != myend) + goto err; + else + upb_sink_putdouble(&p->top->sink, parser_getsel(p), val); + break; + } + case UPB_TYPE_FLOAT: { + float val = strtod(p->accumulated, &end); + if (errno == ERANGE || end != myend) + goto err; + else + upb_sink_putfloat(&p->top->sink, parser_getsel(p), val); + break; + } + default: + assert(false); + } + + multipart_end(p); + + return true; + +err: + upb_status_seterrf(&p->status, "error parsing number: %s", buf); + upb_env_reporterror(p->env, &p->status); + multipart_end(p); + return false; +} + +static bool parser_putbool(upb_json_parser *p, bool val) { + bool ok; + + if (upb_fielddef_type(p->top->f) != UPB_TYPE_BOOL) { + upb_status_seterrf(&p->status, + "Boolean value specified for non-bool field: %s", + upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); + return false; + } + + ok = upb_sink_putbool(&p->top->sink, parser_getsel(p), val); + UPB_ASSERT_VAR(ok, ok); + + return true; +} + +static bool start_stringval(upb_json_parser *p) { + assert(p->top->f); + + if (upb_fielddef_isstring(p->top->f)) { + upb_jsonparser_frame *inner; + upb_selector_t sel; + + if (!check_stack(p)) return false; + + /* Start a new parser frame: parser frames correspond one-to-one with + * handler frames, and string events occur in a sub-frame. */ + inner = p->top + 1; + sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSTR); + upb_sink_startstr(&p->top->sink, sel, 0, &inner->sink); + inner->m = p->top->m; + inner->f = p->top->f; + inner->is_map = false; + inner->is_mapentry = false; + p->top = inner; + + if (upb_fielddef_type(p->top->f) == UPB_TYPE_STRING) { + /* For STRING fields we push data directly to the handlers as it is + * parsed. We don't do this yet for BYTES fields, because our base64 + * decoder is not streaming. + * + * TODO(haberman): make base64 decoding streaming also. */ + multipart_start(p, getsel_for_handlertype(p, UPB_HANDLER_STRING)); + return true; + } else { + multipart_startaccum(p); + return true; + } + } else if (upb_fielddef_type(p->top->f) == UPB_TYPE_ENUM) { + /* No need to push a frame -- symbolic enum names in quotes remain in the + * current parser frame. + * + * Enum string values must accumulate so we can look up the value in a table + * once it is complete. */ + multipart_startaccum(p); + return true; + } else { + upb_status_seterrf(&p->status, + "String specified for non-string/non-enum field: %s", + upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); + return false; + } +} + +static bool end_stringval(upb_json_parser *p) { + bool ok = true; + + switch (upb_fielddef_type(p->top->f)) { + case UPB_TYPE_BYTES: + if (!base64_push(p, getsel_for_handlertype(p, UPB_HANDLER_STRING), + p->accumulated, p->accumulated_len)) { + return false; + } + /* Fall through. */ + + case UPB_TYPE_STRING: { + upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSTR); + upb_sink_endstr(&p->top->sink, sel); + p->top--; + break; + } + + case UPB_TYPE_ENUM: { + /* Resolve enum symbolic name to integer value. */ + const upb_enumdef *enumdef = + (const upb_enumdef*)upb_fielddef_subdef(p->top->f); + + size_t len; + const char *buf = accumulate_getptr(p, &len); + + int32_t int_val = 0; + ok = upb_enumdef_ntoi(enumdef, buf, len, &int_val); + + if (ok) { + upb_selector_t sel = parser_getsel(p); + upb_sink_putint32(&p->top->sink, sel, int_val); + } else { + upb_status_seterrf(&p->status, "Enum value unknown: '%.*s'", len, buf); + upb_env_reporterror(p->env, &p->status); + } + + break; + } + + default: + assert(false); + upb_status_seterrmsg(&p->status, "Internal error in JSON decoder"); + upb_env_reporterror(p->env, &p->status); + ok = false; + break; + } + + multipart_end(p); + + return ok; +} + +static void start_member(upb_json_parser *p) { + assert(!p->top->f); + multipart_startaccum(p); +} + +/* Helper: invoked during parse_mapentry() to emit the mapentry message's key + * field based on the current contents of the accumulate buffer. */ +static bool parse_mapentry_key(upb_json_parser *p) { + + size_t len; + const char *buf = accumulate_getptr(p, &len); + + /* Emit the key field. We do a bit of ad-hoc parsing here because the + * parser state machine has already decided that this is a string field + * name, and we are reinterpreting it as some arbitrary key type. In + * particular, integer and bool keys are quoted, so we need to parse the + * quoted string contents here. */ + + p->top->f = upb_msgdef_itof(p->top->m, UPB_MAPENTRY_KEY); + if (p->top->f == NULL) { + upb_status_seterrmsg(&p->status, "mapentry message has no key"); + upb_env_reporterror(p->env, &p->status); + return false; + } + switch (upb_fielddef_type(p->top->f)) { + case UPB_TYPE_INT32: + case UPB_TYPE_INT64: + case UPB_TYPE_UINT32: + case UPB_TYPE_UINT64: + /* Invoke end_number. The accum buffer has the number's text already. */ + if (!parse_number(p)) { + return false; + } + break; + case UPB_TYPE_BOOL: + if (len == 4 && !strncmp(buf, "true", 4)) { + if (!parser_putbool(p, true)) { + return false; + } + } else if (len == 5 && !strncmp(buf, "false", 5)) { + if (!parser_putbool(p, false)) { + return false; + } + } else { + upb_status_seterrmsg(&p->status, + "Map bool key not 'true' or 'false'"); + upb_env_reporterror(p->env, &p->status); + return false; + } + multipart_end(p); + break; + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: { + upb_sink subsink; + upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSTR); + upb_sink_startstr(&p->top->sink, sel, len, &subsink); + sel = getsel_for_handlertype(p, UPB_HANDLER_STRING); + upb_sink_putstring(&subsink, sel, buf, len, NULL); + sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSTR); + upb_sink_endstr(&subsink, sel); + multipart_end(p); + break; + } + default: + upb_status_seterrmsg(&p->status, "Invalid field type for map key"); + upb_env_reporterror(p->env, &p->status); + return false; + } + + return true; +} + +/* Helper: emit one map entry (as a submessage in the map field sequence). This + * is invoked from end_membername(), at the end of the map entry's key string, + * with the map key in the accumulate buffer. It parses the key from that + * buffer, emits the handler calls to start the mapentry submessage (setting up + * its subframe in the process), and sets up state in the subframe so that the + * value parser (invoked next) will emit the mapentry's value field and then + * end the mapentry message. */ + +static bool handle_mapentry(upb_json_parser *p) { + const upb_fielddef *mapfield; + const upb_msgdef *mapentrymsg; + upb_jsonparser_frame *inner; + upb_selector_t sel; + + /* Map entry: p->top->sink is the seq frame, so we need to start a frame + * for the mapentry itself, and then set |f| in that frame so that the map + * value field is parsed, and also set a flag to end the frame after the + * map-entry value is parsed. */ + if (!check_stack(p)) return false; + + mapfield = p->top->mapfield; + mapentrymsg = upb_fielddef_msgsubdef(mapfield); + + inner = p->top + 1; + p->top->f = mapfield; + sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSUBMSG); + upb_sink_startsubmsg(&p->top->sink, sel, &inner->sink); + inner->m = mapentrymsg; + inner->mapfield = mapfield; + inner->is_map = false; + + /* Don't set this to true *yet* -- we reuse parsing handlers below to push + * the key field value to the sink, and these handlers will pop the frame + * if they see is_mapentry (when invoked by the parser state machine, they + * would have just seen the map-entry value, not key). */ + inner->is_mapentry = false; + p->top = inner; + + /* send STARTMSG in submsg frame. */ + upb_sink_startmsg(&p->top->sink); + + parse_mapentry_key(p); + + /* Set up the value field to receive the map-entry value. */ + p->top->f = upb_msgdef_itof(p->top->m, UPB_MAPENTRY_VALUE); + p->top->is_mapentry = true; /* set up to pop frame after value is parsed. */ + p->top->mapfield = mapfield; + if (p->top->f == NULL) { + upb_status_seterrmsg(&p->status, "mapentry message has no value"); + upb_env_reporterror(p->env, &p->status); + return false; + } + + return true; +} + +static bool end_membername(upb_json_parser *p) { + assert(!p->top->f); + + if (p->top->is_map) { + return handle_mapentry(p); + } else { + size_t len; + const char *buf = accumulate_getptr(p, &len); + const upb_fielddef *f = upb_msgdef_ntof(p->top->m, buf, len); + + if (!f) { + /* TODO(haberman): Ignore unknown fields if requested/configured to do + * so. */ + upb_status_seterrf(&p->status, "No such field: %.*s\n", (int)len, buf); + upb_env_reporterror(p->env, &p->status); + return false; + } + + p->top->f = f; + multipart_end(p); + + return true; + } +} + +static void end_member(upb_json_parser *p) { + /* If we just parsed a map-entry value, end that frame too. */ + if (p->top->is_mapentry) { + upb_status s = UPB_STATUS_INIT; + upb_selector_t sel; + bool ok; + const upb_fielddef *mapfield; + + assert(p->top > p->stack); + /* send ENDMSG on submsg. */ + upb_sink_endmsg(&p->top->sink, &s); + mapfield = p->top->mapfield; + + /* send ENDSUBMSG in repeated-field-of-mapentries frame. */ + p->top--; + ok = upb_handlers_getselector(mapfield, UPB_HANDLER_ENDSUBMSG, &sel); + UPB_ASSERT_VAR(ok, ok); + upb_sink_endsubmsg(&p->top->sink, sel); + } + + p->top->f = NULL; +} + +static bool start_subobject(upb_json_parser *p) { + assert(p->top->f); + + if (upb_fielddef_ismap(p->top->f)) { + upb_jsonparser_frame *inner; + upb_selector_t sel; + + /* Beginning of a map. Start a new parser frame in a repeated-field + * context. */ + if (!check_stack(p)) return false; + + inner = p->top + 1; + sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSEQ); + upb_sink_startseq(&p->top->sink, sel, &inner->sink); + inner->m = upb_fielddef_msgsubdef(p->top->f); + inner->mapfield = p->top->f; + inner->f = NULL; + inner->is_map = true; + inner->is_mapentry = false; + p->top = inner; + + return true; + } else if (upb_fielddef_issubmsg(p->top->f)) { + upb_jsonparser_frame *inner; + upb_selector_t sel; + + /* Beginning of a subobject. Start a new parser frame in the submsg + * context. */ + if (!check_stack(p)) return false; + + inner = p->top + 1; + + sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSUBMSG); + upb_sink_startsubmsg(&p->top->sink, sel, &inner->sink); + inner->m = upb_fielddef_msgsubdef(p->top->f); + inner->f = NULL; + inner->is_map = false; + inner->is_mapentry = false; + p->top = inner; + + return true; + } else { + upb_status_seterrf(&p->status, + "Object specified for non-message/group field: %s", + upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); + return false; + } +} + +static void end_subobject(upb_json_parser *p) { + if (p->top->is_map) { + upb_selector_t sel; + p->top--; + sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSEQ); + upb_sink_endseq(&p->top->sink, sel); + } else { + upb_selector_t sel; + p->top--; + sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSUBMSG); + upb_sink_endsubmsg(&p->top->sink, sel); + } +} + +static bool start_array(upb_json_parser *p) { + upb_jsonparser_frame *inner; + upb_selector_t sel; + + assert(p->top->f); + + if (!upb_fielddef_isseq(p->top->f)) { + upb_status_seterrf(&p->status, + "Array specified for non-repeated field: %s", + upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); + return false; + } + + if (!check_stack(p)) return false; + + inner = p->top + 1; + sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSEQ); + upb_sink_startseq(&p->top->sink, sel, &inner->sink); + inner->m = p->top->m; + inner->f = p->top->f; + inner->is_map = false; + inner->is_mapentry = false; + p->top = inner; + + return true; +} + +static void end_array(upb_json_parser *p) { + upb_selector_t sel; + + assert(p->top > p->stack); + + p->top--; + sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSEQ); + upb_sink_endseq(&p->top->sink, sel); +} + +static void start_object(upb_json_parser *p) { + if (!p->top->is_map) { + upb_sink_startmsg(&p->top->sink); + } +} + +static void end_object(upb_json_parser *p) { + if (!p->top->is_map) { + upb_status status; + upb_status_clear(&status); + upb_sink_endmsg(&p->top->sink, &status); + if (!upb_ok(&status)) { + upb_env_reporterror(p->env, &status); + } + } +} + + +#define CHECK_RETURN_TOP(x) if (!(x)) goto error + + +/* The actual parser **********************************************************/ + +/* What follows is the Ragel parser itself. The language is specified in Ragel + * and the actions call our C functions above. + * + * Ragel has an extensive set of functionality, and we use only a small part of + * it. There are many action types but we only use a few: + * + * ">" -- transition into a machine + * "%" -- transition out of a machine + * "@" -- transition into a final state of a machine. + * + * "@" transitions are tricky because a machine can transition into a final + * state repeatedly. But in some cases we know this can't happen, for example + * a string which is delimited by a final '"' can only transition into its + * final state once, when the closing '"' is seen. */ + + +#line 1218 "upb/json/parser.rl" + + + +#line 1130 "upb/json/parser.c" +static const char _json_actions[] = { + 0, 1, 0, 1, 2, 1, 3, 1, + 5, 1, 6, 1, 7, 1, 8, 1, + 10, 1, 12, 1, 13, 1, 14, 1, + 15, 1, 16, 1, 17, 1, 21, 1, + 25, 1, 27, 2, 3, 8, 2, 4, + 5, 2, 6, 2, 2, 6, 8, 2, + 11, 9, 2, 13, 15, 2, 14, 15, + 2, 18, 1, 2, 19, 27, 2, 20, + 9, 2, 22, 27, 2, 23, 27, 2, + 24, 27, 2, 26, 27, 3, 14, 11, + 9 +}; + +static const unsigned char _json_key_offsets[] = { + 0, 0, 4, 9, 14, 15, 19, 24, + 29, 34, 38, 42, 45, 48, 50, 54, + 58, 60, 62, 67, 69, 71, 80, 86, + 92, 98, 104, 106, 115, 116, 116, 116, + 121, 126, 131, 132, 133, 134, 135, 135, + 136, 137, 138, 138, 139, 140, 141, 141, + 146, 151, 152, 156, 161, 166, 171, 175, + 175, 178, 178, 178 +}; + +static const char _json_trans_keys[] = { + 32, 123, 9, 13, 32, 34, 125, 9, + 13, 32, 34, 125, 9, 13, 34, 32, + 58, 9, 13, 32, 93, 125, 9, 13, + 32, 44, 125, 9, 13, 32, 44, 125, + 9, 13, 32, 34, 9, 13, 45, 48, + 49, 57, 48, 49, 57, 46, 69, 101, + 48, 57, 69, 101, 48, 57, 43, 45, + 48, 57, 48, 57, 48, 57, 46, 69, + 101, 48, 57, 34, 92, 34, 92, 34, + 47, 92, 98, 102, 110, 114, 116, 117, + 48, 57, 65, 70, 97, 102, 48, 57, + 65, 70, 97, 102, 48, 57, 65, 70, + 97, 102, 48, 57, 65, 70, 97, 102, + 34, 92, 34, 45, 91, 102, 110, 116, + 123, 48, 57, 34, 32, 93, 125, 9, + 13, 32, 44, 93, 9, 13, 32, 93, + 125, 9, 13, 97, 108, 115, 101, 117, + 108, 108, 114, 117, 101, 32, 34, 125, + 9, 13, 32, 34, 125, 9, 13, 34, + 32, 58, 9, 13, 32, 93, 125, 9, + 13, 32, 44, 125, 9, 13, 32, 44, + 125, 9, 13, 32, 34, 9, 13, 32, + 9, 13, 0 +}; + +static const char _json_single_lengths[] = { + 0, 2, 3, 3, 1, 2, 3, 3, + 3, 2, 2, 1, 3, 0, 2, 2, + 0, 0, 3, 2, 2, 9, 0, 0, + 0, 0, 2, 7, 1, 0, 0, 3, + 3, 3, 1, 1, 1, 1, 0, 1, + 1, 1, 0, 1, 1, 1, 0, 3, + 3, 1, 2, 3, 3, 3, 2, 0, + 1, 0, 0, 0 +}; + +static const char _json_range_lengths[] = { + 0, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 3, 3, + 3, 3, 0, 1, 0, 0, 0, 1, + 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 1, 1, 1, 1, 1, 0, + 1, 0, 0, 0 +}; + +static const short _json_index_offsets[] = { + 0, 0, 4, 9, 14, 16, 20, 25, + 30, 35, 39, 43, 46, 50, 52, 56, + 60, 62, 64, 69, 72, 75, 85, 89, + 93, 97, 101, 104, 113, 115, 116, 117, + 122, 127, 132, 134, 136, 138, 140, 141, + 143, 145, 147, 148, 150, 152, 154, 155, + 160, 165, 167, 171, 176, 181, 186, 190, + 191, 194, 195, 196 +}; + +static const char _json_indicies[] = { + 0, 2, 0, 1, 3, 4, 5, 3, + 1, 6, 7, 8, 6, 1, 9, 1, + 10, 11, 10, 1, 11, 1, 1, 11, + 12, 13, 14, 15, 13, 1, 16, 17, + 8, 16, 1, 17, 7, 17, 1, 18, + 19, 20, 1, 19, 20, 1, 22, 23, + 23, 21, 24, 1, 23, 23, 24, 21, + 25, 25, 26, 1, 26, 1, 26, 21, + 22, 23, 23, 20, 21, 28, 29, 27, + 31, 32, 30, 33, 33, 33, 33, 33, + 33, 33, 33, 34, 1, 35, 35, 35, + 1, 36, 36, 36, 1, 37, 37, 37, + 1, 38, 38, 38, 1, 40, 41, 39, + 42, 43, 44, 45, 46, 47, 48, 43, + 1, 49, 1, 50, 51, 53, 54, 1, + 53, 52, 55, 56, 54, 55, 1, 56, + 1, 1, 56, 52, 57, 1, 58, 1, + 59, 1, 60, 1, 61, 62, 1, 63, + 1, 64, 1, 65, 66, 1, 67, 1, + 68, 1, 69, 70, 71, 72, 70, 1, + 73, 74, 75, 73, 1, 76, 1, 77, + 78, 77, 1, 78, 1, 1, 78, 79, + 80, 81, 82, 80, 1, 83, 84, 75, + 83, 1, 84, 74, 84, 1, 85, 86, + 86, 1, 1, 1, 1, 0 +}; + +static const char _json_trans_targs[] = { + 1, 0, 2, 3, 4, 56, 3, 4, + 56, 5, 5, 6, 7, 8, 9, 56, + 8, 9, 11, 12, 18, 57, 13, 15, + 14, 16, 17, 20, 58, 21, 20, 58, + 21, 19, 22, 23, 24, 25, 26, 20, + 58, 21, 28, 30, 31, 34, 39, 43, + 47, 29, 59, 59, 32, 31, 29, 32, + 33, 35, 36, 37, 38, 59, 40, 41, + 42, 59, 44, 45, 46, 59, 48, 49, + 55, 48, 49, 55, 50, 50, 51, 52, + 53, 54, 55, 53, 54, 59, 56 +}; + +static const char _json_trans_actions[] = { + 0, 0, 0, 21, 77, 53, 0, 47, + 23, 17, 0, 0, 15, 19, 19, 50, + 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 3, 13, 0, 0, 35, + 5, 11, 0, 38, 7, 7, 7, 41, + 44, 9, 62, 56, 25, 0, 0, 0, + 31, 29, 33, 59, 15, 0, 27, 0, + 0, 0, 0, 0, 0, 68, 0, 0, + 0, 71, 0, 0, 0, 65, 21, 77, + 53, 0, 47, 23, 17, 0, 0, 15, + 19, 19, 50, 0, 0, 74, 0 +}; + +static const int json_start = 1; + +static const int json_en_number_machine = 10; +static const int json_en_string_machine = 19; +static const int json_en_value_machine = 27; +static const int json_en_main = 1; + + +#line 1221 "upb/json/parser.rl" + +size_t parse(void *closure, const void *hd, const char *buf, size_t size, + const upb_bufhandle *handle) { + upb_json_parser *parser = closure; + + /* Variables used by Ragel's generated code. */ + int cs = parser->current_state; + int *stack = parser->parser_stack; + int top = parser->parser_top; + + const char *p = buf; + const char *pe = buf + size; + + parser->handle = handle; + + UPB_UNUSED(hd); + UPB_UNUSED(handle); + + capture_resume(parser, buf); + + +#line 1301 "upb/json/parser.c" + { + int _klen; + unsigned int _trans; + const char *_acts; + unsigned int _nacts; + const char *_keys; + + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _json_trans_keys + _json_key_offsets[cs]; + _trans = _json_index_offsets[cs]; + + _klen = _json_single_lengths[cs]; + if ( _klen > 0 ) { + const char *_lower = _keys; + const char *_mid; + const char *_upper = _keys + _klen - 1; + while (1) { + if ( _upper < _lower ) + break; + + _mid = _lower + ((_upper-_lower) >> 1); + if ( (*p) < *_mid ) + _upper = _mid - 1; + else if ( (*p) > *_mid ) + _lower = _mid + 1; + else { + _trans += (unsigned int)(_mid - _keys); + goto _match; + } + } + _keys += _klen; + _trans += _klen; + } + + _klen = _json_range_lengths[cs]; + if ( _klen > 0 ) { + const char *_lower = _keys; + const char *_mid; + const char *_upper = _keys + (_klen<<1) - 2; + while (1) { + if ( _upper < _lower ) + break; + + _mid = _lower + (((_upper-_lower) >> 1) & ~1); + if ( (*p) < _mid[0] ) + _upper = _mid - 2; + else if ( (*p) > _mid[1] ) + _lower = _mid + 2; + else { + _trans += (unsigned int)((_mid - _keys)>>1); + goto _match; + } + } + _trans += _klen; + } + +_match: + _trans = _json_indicies[_trans]; + cs = _json_trans_targs[_trans]; + + if ( _json_trans_actions[_trans] == 0 ) + goto _again; + + _acts = _json_actions + _json_trans_actions[_trans]; + _nacts = (unsigned int) *_acts++; + while ( _nacts-- > 0 ) + { + switch ( *_acts++ ) + { + case 0: +#line 1133 "upb/json/parser.rl" + { p--; {cs = stack[--top]; goto _again;} } + break; + case 1: +#line 1134 "upb/json/parser.rl" + { p--; {stack[top++] = cs; cs = 10; goto _again;} } + break; + case 2: +#line 1138 "upb/json/parser.rl" + { start_text(parser, p); } + break; + case 3: +#line 1139 "upb/json/parser.rl" + { CHECK_RETURN_TOP(end_text(parser, p)); } + break; + case 4: +#line 1145 "upb/json/parser.rl" + { start_hex(parser); } + break; + case 5: +#line 1146 "upb/json/parser.rl" + { hexdigit(parser, p); } + break; + case 6: +#line 1147 "upb/json/parser.rl" + { CHECK_RETURN_TOP(end_hex(parser)); } + break; + case 7: +#line 1153 "upb/json/parser.rl" + { CHECK_RETURN_TOP(escape(parser, p)); } + break; + case 8: +#line 1159 "upb/json/parser.rl" + { p--; {cs = stack[--top]; goto _again;} } + break; + case 9: +#line 1162 "upb/json/parser.rl" + { {stack[top++] = cs; cs = 19; goto _again;} } + break; + case 10: +#line 1164 "upb/json/parser.rl" + { p--; {stack[top++] = cs; cs = 27; goto _again;} } + break; + case 11: +#line 1169 "upb/json/parser.rl" + { start_member(parser); } + break; + case 12: +#line 1170 "upb/json/parser.rl" + { CHECK_RETURN_TOP(end_membername(parser)); } + break; + case 13: +#line 1173 "upb/json/parser.rl" + { end_member(parser); } + break; + case 14: +#line 1179 "upb/json/parser.rl" + { start_object(parser); } + break; + case 15: +#line 1182 "upb/json/parser.rl" + { end_object(parser); } + break; + case 16: +#line 1188 "upb/json/parser.rl" + { CHECK_RETURN_TOP(start_array(parser)); } + break; + case 17: +#line 1192 "upb/json/parser.rl" + { end_array(parser); } + break; + case 18: +#line 1197 "upb/json/parser.rl" + { start_number(parser, p); } + break; + case 19: +#line 1198 "upb/json/parser.rl" + { CHECK_RETURN_TOP(end_number(parser, p)); } + break; + case 20: +#line 1200 "upb/json/parser.rl" + { CHECK_RETURN_TOP(start_stringval(parser)); } + break; + case 21: +#line 1201 "upb/json/parser.rl" + { CHECK_RETURN_TOP(end_stringval(parser)); } + break; + case 22: +#line 1203 "upb/json/parser.rl" + { CHECK_RETURN_TOP(parser_putbool(parser, true)); } + break; + case 23: +#line 1205 "upb/json/parser.rl" + { CHECK_RETURN_TOP(parser_putbool(parser, false)); } + break; + case 24: +#line 1207 "upb/json/parser.rl" + { /* null value */ } + break; + case 25: +#line 1209 "upb/json/parser.rl" + { CHECK_RETURN_TOP(start_subobject(parser)); } + break; + case 26: +#line 1210 "upb/json/parser.rl" + { end_subobject(parser); } + break; + case 27: +#line 1215 "upb/json/parser.rl" + { p--; {cs = stack[--top]; goto _again;} } + break; +#line 1487 "upb/json/parser.c" + } + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + _out: {} + } + +#line 1242 "upb/json/parser.rl" + + if (p != pe) { + upb_status_seterrf(&parser->status, "Parse error at %s\n", p); + upb_env_reporterror(parser->env, &parser->status); + } else { + capture_suspend(parser, &p); + } + +error: + /* Save parsing state back to parser. */ + parser->current_state = cs; + parser->parser_top = top; + + return p - buf; +} + +bool end(void *closure, const void *hd) { + UPB_UNUSED(closure); + UPB_UNUSED(hd); + + /* Prevent compile warning on unused static constants. */ + UPB_UNUSED(json_start); + UPB_UNUSED(json_en_number_machine); + UPB_UNUSED(json_en_string_machine); + UPB_UNUSED(json_en_value_machine); + UPB_UNUSED(json_en_main); + return true; +} + +static void json_parser_reset(upb_json_parser *p) { + int cs; + int top; + + p->top = p->stack; + p->top->f = NULL; + p->top->is_map = false; + p->top->is_mapentry = false; + + /* Emit Ragel initialization of the parser. */ + +#line 1541 "upb/json/parser.c" + { + cs = json_start; + top = 0; + } + +#line 1282 "upb/json/parser.rl" + p->current_state = cs; + p->parser_top = top; + accumulate_clear(p); + p->multipart_state = MULTIPART_INACTIVE; + p->capture = NULL; + p->accumulated = NULL; + upb_status_clear(&p->status); +} + + +/* Public API *****************************************************************/ + +upb_json_parser *upb_json_parser_create(upb_env *env, upb_sink *output) { +#ifndef NDEBUG + const size_t size_before = upb_env_bytesallocated(env); +#endif + upb_json_parser *p = upb_env_malloc(env, sizeof(upb_json_parser)); + if (!p) return false; + + p->env = env; + p->limit = p->stack + UPB_JSON_MAX_DEPTH; + p->accumulate_buf = NULL; + p->accumulate_buf_size = 0; + upb_byteshandler_init(&p->input_handler_); + upb_byteshandler_setstring(&p->input_handler_, parse, NULL); + upb_byteshandler_setendstr(&p->input_handler_, end, NULL); + upb_bytessink_reset(&p->input_, &p->input_handler_, p); + + json_parser_reset(p); + upb_sink_reset(&p->top->sink, output->handlers, output->closure); + p->top->m = upb_handlers_msgdef(output->handlers); + + /* If this fails, uncomment and increase the value in parser.h. */ + /* fprintf(stderr, "%zd\n", upb_env_bytesallocated(env) - size_before); */ + assert(upb_env_bytesallocated(env) - size_before <= UPB_JSON_PARSER_SIZE); + return p; +} + +upb_bytessink *upb_json_parser_input(upb_json_parser *p) { + return &p->input_; +} +/* +** This currently uses snprintf() to format primitives, and could be optimized +** further. +*/ + + +#include +#include +#include +#include + +struct upb_json_printer { + upb_sink input_; + /* BytesSink closure. */ + void *subc_; + upb_bytessink *output_; + + /* We track the depth so that we know when to emit startstr/endstr on the + * output. */ + int depth_; + + /* Have we emitted the first element? This state is necessary to emit commas + * without leaving a trailing comma in arrays/maps. We keep this state per + * frame depth. + * + * Why max_depth * 2? UPB_MAX_HANDLER_DEPTH counts depth as nested messages. + * We count frames (contexts in which we separate elements by commas) as both + * repeated fields and messages (maps), and the worst case is a + * message->repeated field->submessage->repeated field->... nesting. */ + bool first_elem_[UPB_MAX_HANDLER_DEPTH * 2]; +}; + +/* StringPiece; a pointer plus a length. */ +typedef struct { + const char *ptr; + size_t len; +} strpc; + +strpc *newstrpc(upb_handlers *h, const upb_fielddef *f) { + strpc *ret = malloc(sizeof(*ret)); + ret->ptr = upb_fielddef_name(f); + ret->len = strlen(ret->ptr); + upb_handlers_addcleanup(h, ret, free); + return ret; +} + +/* ------------ JSON string printing: values, maps, arrays ------------------ */ + +static void print_data( + upb_json_printer *p, const char *buf, unsigned int len) { + /* TODO: Will need to change if we support pushback from the sink. */ + size_t n = upb_bytessink_putbuf(p->output_, p->subc_, buf, len, NULL); + UPB_ASSERT_VAR(n, n == len); +} + +static void print_comma(upb_json_printer *p) { + if (!p->first_elem_[p->depth_]) { + print_data(p, ",", 1); + } + p->first_elem_[p->depth_] = false; +} + +/* Helpers that print properly formatted elements to the JSON output stream. */ + +/* Used for escaping control chars in strings. */ +static const char kControlCharLimit = 0x20; + +UPB_INLINE bool is_json_escaped(char c) { + /* See RFC 4627. */ + unsigned char uc = (unsigned char)c; + return uc < kControlCharLimit || uc == '"' || uc == '\\'; +} + +UPB_INLINE char* json_nice_escape(char c) { + switch (c) { + case '"': return "\\\""; + case '\\': return "\\\\"; + case '\b': return "\\b"; + case '\f': return "\\f"; + case '\n': return "\\n"; + case '\r': return "\\r"; + case '\t': return "\\t"; + default: return NULL; + } +} + +/* Write a properly escaped string chunk. The surrounding quotes are *not* + * printed; this is so that the caller has the option of emitting the string + * content in chunks. */ +static void putstring(upb_json_printer *p, const char *buf, unsigned int len) { + const char* unescaped_run = NULL; + unsigned int i; + for (i = 0; i < len; i++) { + char c = buf[i]; + /* Handle escaping. */ + if (is_json_escaped(c)) { + /* Use a "nice" escape, like \n, if one exists for this character. */ + const char* escape = json_nice_escape(c); + /* If we don't have a specific 'nice' escape code, use a \uXXXX-style + * escape. */ + char escape_buf[8]; + if (!escape) { + unsigned char byte = (unsigned char)c; + _upb_snprintf(escape_buf, sizeof(escape_buf), "\\u%04x", (int)byte); + escape = escape_buf; + } + + /* N.B. that we assume that the input encoding is equal to the output + * encoding (both UTF-8 for now), so for chars >= 0x20 and != \, ", we + * can simply pass the bytes through. */ + + /* If there's a current run of unescaped chars, print that run first. */ + if (unescaped_run) { + print_data(p, unescaped_run, &buf[i] - unescaped_run); + unescaped_run = NULL; + } + /* Then print the escape code. */ + print_data(p, escape, strlen(escape)); + } else { + /* Add to the current unescaped run of characters. */ + if (unescaped_run == NULL) { + unescaped_run = &buf[i]; + } + } + } + + /* If the string ended in a run of unescaped characters, print that last run. */ + if (unescaped_run) { + print_data(p, unescaped_run, &buf[len] - unescaped_run); + } +} + +#define CHKLENGTH(x) if (!(x)) return -1; + +/* Helpers that format floating point values according to our custom formats. + * Right now we use %.8g and %.17g for float/double, respectively, to match + * proto2::util::JsonFormat's defaults. May want to change this later. */ + +static size_t fmt_double(double val, char* buf, size_t length) { + size_t n = _upb_snprintf(buf, length, "%.17g", val); + CHKLENGTH(n > 0 && n < length); + return n; +} + +static size_t fmt_float(float val, char* buf, size_t length) { + size_t n = _upb_snprintf(buf, length, "%.8g", val); + CHKLENGTH(n > 0 && n < length); + return n; +} + +static size_t fmt_bool(bool val, char* buf, size_t length) { + size_t n = _upb_snprintf(buf, length, "%s", (val ? "true" : "false")); + CHKLENGTH(n > 0 && n < length); + return n; +} + +static size_t fmt_int64(long val, char* buf, size_t length) { + size_t n = _upb_snprintf(buf, length, "%ld", val); + CHKLENGTH(n > 0 && n < length); + return n; +} + +static size_t fmt_uint64(unsigned long long val, char* buf, size_t length) { + size_t n = _upb_snprintf(buf, length, "%llu", val); + CHKLENGTH(n > 0 && n < length); + return n; +} + +/* Print a map key given a field name. Called by scalar field handlers and by + * startseq for repeated fields. */ +static bool putkey(void *closure, const void *handler_data) { + upb_json_printer *p = closure; + const strpc *key = handler_data; + print_comma(p); + print_data(p, "\"", 1); + putstring(p, key->ptr, key->len); + print_data(p, "\":", 2); + return true; +} + +#define CHKFMT(val) if ((val) == (size_t)-1) return false; +#define CHK(val) if (!(val)) return false; + +#define TYPE_HANDLERS(type, fmt_func) \ + static bool put##type(void *closure, const void *handler_data, type val) { \ + upb_json_printer *p = closure; \ + char data[64]; \ + size_t length = fmt_func(val, data, sizeof(data)); \ + UPB_UNUSED(handler_data); \ + CHKFMT(length); \ + print_data(p, data, length); \ + return true; \ + } \ + static bool scalar_##type(void *closure, const void *handler_data, \ + type val) { \ + CHK(putkey(closure, handler_data)); \ + CHK(put##type(closure, handler_data, val)); \ + return true; \ + } \ + static bool repeated_##type(void *closure, const void *handler_data, \ + type val) { \ + upb_json_printer *p = closure; \ + print_comma(p); \ + CHK(put##type(closure, handler_data, val)); \ + return true; \ + } + +#define TYPE_HANDLERS_MAPKEY(type, fmt_func) \ + static bool putmapkey_##type(void *closure, const void *handler_data, \ + type val) { \ + upb_json_printer *p = closure; \ + print_data(p, "\"", 1); \ + CHK(put##type(closure, handler_data, val)); \ + print_data(p, "\":", 2); \ + return true; \ + } + +TYPE_HANDLERS(double, fmt_double) +TYPE_HANDLERS(float, fmt_float) +TYPE_HANDLERS(bool, fmt_bool) +TYPE_HANDLERS(int32_t, fmt_int64) +TYPE_HANDLERS(uint32_t, fmt_int64) +TYPE_HANDLERS(int64_t, fmt_int64) +TYPE_HANDLERS(uint64_t, fmt_uint64) + +/* double and float are not allowed to be map keys. */ +TYPE_HANDLERS_MAPKEY(bool, fmt_bool) +TYPE_HANDLERS_MAPKEY(int32_t, fmt_int64) +TYPE_HANDLERS_MAPKEY(uint32_t, fmt_int64) +TYPE_HANDLERS_MAPKEY(int64_t, fmt_int64) +TYPE_HANDLERS_MAPKEY(uint64_t, fmt_uint64) + +#undef TYPE_HANDLERS +#undef TYPE_HANDLERS_MAPKEY + +typedef struct { + void *keyname; + const upb_enumdef *enumdef; +} EnumHandlerData; + +static bool scalar_enum(void *closure, const void *handler_data, + int32_t val) { + const EnumHandlerData *hd = handler_data; + upb_json_printer *p = closure; + const char *symbolic_name; + + CHK(putkey(closure, hd->keyname)); + + symbolic_name = upb_enumdef_iton(hd->enumdef, val); + if (symbolic_name) { + print_data(p, "\"", 1); + putstring(p, symbolic_name, strlen(symbolic_name)); + print_data(p, "\"", 1); + } else { + putint32_t(closure, NULL, val); + } + + return true; +} + +static void print_enum_symbolic_name(upb_json_printer *p, + const upb_enumdef *def, + int32_t val) { + const char *symbolic_name = upb_enumdef_iton(def, val); + if (symbolic_name) { + print_data(p, "\"", 1); + putstring(p, symbolic_name, strlen(symbolic_name)); + print_data(p, "\"", 1); + } else { + putint32_t(p, NULL, val); + } +} + +static bool repeated_enum(void *closure, const void *handler_data, + int32_t val) { + const EnumHandlerData *hd = handler_data; + upb_json_printer *p = closure; + print_comma(p); + + print_enum_symbolic_name(p, hd->enumdef, val); + + return true; +} + +static bool mapvalue_enum(void *closure, const void *handler_data, + int32_t val) { + const EnumHandlerData *hd = handler_data; + upb_json_printer *p = closure; + + print_enum_symbolic_name(p, hd->enumdef, val); + + return true; +} + +static void *scalar_startsubmsg(void *closure, const void *handler_data) { + return putkey(closure, handler_data) ? closure : UPB_BREAK; +} + +static void *repeated_startsubmsg(void *closure, const void *handler_data) { + upb_json_printer *p = closure; + UPB_UNUSED(handler_data); + print_comma(p); + return closure; +} + +static void start_frame(upb_json_printer *p) { + p->depth_++; + p->first_elem_[p->depth_] = true; + print_data(p, "{", 1); +} + +static void end_frame(upb_json_printer *p) { + print_data(p, "}", 1); + p->depth_--; +} + +static bool printer_startmsg(void *closure, const void *handler_data) { + upb_json_printer *p = closure; + UPB_UNUSED(handler_data); + if (p->depth_ == 0) { + upb_bytessink_start(p->output_, 0, &p->subc_); + } + start_frame(p); + return true; +} + +static bool printer_endmsg(void *closure, const void *handler_data, upb_status *s) { + upb_json_printer *p = closure; + UPB_UNUSED(handler_data); + UPB_UNUSED(s); + end_frame(p); + if (p->depth_ == 0) { + upb_bytessink_end(p->output_); + } + return true; +} + +static void *startseq(void *closure, const void *handler_data) { + upb_json_printer *p = closure; + CHK(putkey(closure, handler_data)); + p->depth_++; + p->first_elem_[p->depth_] = true; + print_data(p, "[", 1); + return closure; +} + +static bool endseq(void *closure, const void *handler_data) { + upb_json_printer *p = closure; + UPB_UNUSED(handler_data); + print_data(p, "]", 1); + p->depth_--; + return true; +} + +static void *startmap(void *closure, const void *handler_data) { + upb_json_printer *p = closure; + CHK(putkey(closure, handler_data)); + p->depth_++; + p->first_elem_[p->depth_] = true; + print_data(p, "{", 1); + return closure; +} + +static bool endmap(void *closure, const void *handler_data) { + upb_json_printer *p = closure; + UPB_UNUSED(handler_data); + print_data(p, "}", 1); + p->depth_--; + return true; +} + +static size_t putstr(void *closure, const void *handler_data, const char *str, + size_t len, const upb_bufhandle *handle) { + upb_json_printer *p = closure; + UPB_UNUSED(handler_data); + UPB_UNUSED(handle); + putstring(p, str, len); + return len; +} + +/* This has to Base64 encode the bytes, because JSON has no "bytes" type. */ +static size_t putbytes(void *closure, const void *handler_data, const char *str, + size_t len, const upb_bufhandle *handle) { + upb_json_printer *p = closure; + + /* This is the regular base64, not the "web-safe" version. */ + static const char base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + /* Base64-encode. */ + char data[16000]; + const char *limit = data + sizeof(data); + const unsigned char *from = (const unsigned char*)str; + char *to = data; + size_t remaining = len; + size_t bytes; + + UPB_UNUSED(handler_data); + UPB_UNUSED(handle); + + while (remaining > 2) { + /* TODO(haberman): handle encoded lengths > sizeof(data) */ + UPB_ASSERT_VAR(limit, (limit - to) >= 4); + + to[0] = base64[from[0] >> 2]; + to[1] = base64[((from[0] & 0x3) << 4) | (from[1] >> 4)]; + to[2] = base64[((from[1] & 0xf) << 2) | (from[2] >> 6)]; + to[3] = base64[from[2] & 0x3f]; + + remaining -= 3; + to += 4; + from += 3; + } + + switch (remaining) { + case 2: + to[0] = base64[from[0] >> 2]; + to[1] = base64[((from[0] & 0x3) << 4) | (from[1] >> 4)]; + to[2] = base64[(from[1] & 0xf) << 2]; + to[3] = '='; + to += 4; + from += 2; + break; + case 1: + to[0] = base64[from[0] >> 2]; + to[1] = base64[((from[0] & 0x3) << 4)]; + to[2] = '='; + to[3] = '='; + to += 4; + from += 1; + break; + } + + bytes = to - data; + print_data(p, "\"", 1); + putstring(p, data, bytes); + print_data(p, "\"", 1); + return len; +} + +static void *scalar_startstr(void *closure, const void *handler_data, + size_t size_hint) { + upb_json_printer *p = closure; + UPB_UNUSED(handler_data); + UPB_UNUSED(size_hint); + CHK(putkey(closure, handler_data)); + print_data(p, "\"", 1); + return p; +} + +static size_t scalar_str(void *closure, const void *handler_data, + const char *str, size_t len, + const upb_bufhandle *handle) { + CHK(putstr(closure, handler_data, str, len, handle)); + return len; +} + +static bool scalar_endstr(void *closure, const void *handler_data) { + upb_json_printer *p = closure; + UPB_UNUSED(handler_data); + print_data(p, "\"", 1); + return true; +} + +static void *repeated_startstr(void *closure, const void *handler_data, + size_t size_hint) { + upb_json_printer *p = closure; + UPB_UNUSED(handler_data); + UPB_UNUSED(size_hint); + print_comma(p); + print_data(p, "\"", 1); + return p; +} + +static size_t repeated_str(void *closure, const void *handler_data, + const char *str, size_t len, + const upb_bufhandle *handle) { + CHK(putstr(closure, handler_data, str, len, handle)); + return len; +} + +static bool repeated_endstr(void *closure, const void *handler_data) { + upb_json_printer *p = closure; + UPB_UNUSED(handler_data); + print_data(p, "\"", 1); + return true; +} + +static void *mapkeyval_startstr(void *closure, const void *handler_data, + size_t size_hint) { + upb_json_printer *p = closure; + UPB_UNUSED(handler_data); + UPB_UNUSED(size_hint); + print_data(p, "\"", 1); + return p; +} + +static size_t mapkey_str(void *closure, const void *handler_data, + const char *str, size_t len, + const upb_bufhandle *handle) { + CHK(putstr(closure, handler_data, str, len, handle)); + return len; +} + +static bool mapkey_endstr(void *closure, const void *handler_data) { + upb_json_printer *p = closure; + UPB_UNUSED(handler_data); + print_data(p, "\":", 2); + return true; +} + +static bool mapvalue_endstr(void *closure, const void *handler_data) { + upb_json_printer *p = closure; + UPB_UNUSED(handler_data); + print_data(p, "\"", 1); + return true; +} + +static size_t scalar_bytes(void *closure, const void *handler_data, + const char *str, size_t len, + const upb_bufhandle *handle) { + CHK(putkey(closure, handler_data)); + CHK(putbytes(closure, handler_data, str, len, handle)); + return len; +} + +static size_t repeated_bytes(void *closure, const void *handler_data, + const char *str, size_t len, + const upb_bufhandle *handle) { + upb_json_printer *p = closure; + print_comma(p); + CHK(putbytes(closure, handler_data, str, len, handle)); + return len; +} + +static size_t mapkey_bytes(void *closure, const void *handler_data, + const char *str, size_t len, + const upb_bufhandle *handle) { + upb_json_printer *p = closure; + CHK(putbytes(closure, handler_data, str, len, handle)); + print_data(p, ":", 1); + return len; +} + +static void set_enum_hd(upb_handlers *h, + const upb_fielddef *f, + upb_handlerattr *attr) { + EnumHandlerData *hd = malloc(sizeof(EnumHandlerData)); + hd->enumdef = (const upb_enumdef *)upb_fielddef_subdef(f); + hd->keyname = newstrpc(h, f); + upb_handlers_addcleanup(h, hd, free); + upb_handlerattr_sethandlerdata(attr, hd); +} + +/* Set up handlers for a mapentry submessage (i.e., an individual key/value pair + * in a map). + * + * TODO: Handle missing key, missing value, out-of-order key/value, or repeated + * key or value cases properly. The right way to do this is to allocate a + * temporary structure at the start of a mapentry submessage, store key and + * value data in it as key and value handlers are called, and then print the + * key/value pair once at the end of the submessage. If we don't do this, we + * should at least detect the case and throw an error. However, so far all of + * our sources that emit mapentry messages do so canonically (with one key + * field, and then one value field), so this is not a pressing concern at the + * moment. */ +void printer_sethandlers_mapentry(const void *closure, upb_handlers *h) { + const upb_msgdef *md = upb_handlers_msgdef(h); + + /* A mapentry message is printed simply as '"key": value'. Rather than + * special-case key and value for every type below, we just handle both + * fields explicitly here. */ + const upb_fielddef* key_field = upb_msgdef_itof(md, UPB_MAPENTRY_KEY); + const upb_fielddef* value_field = upb_msgdef_itof(md, UPB_MAPENTRY_VALUE); + + upb_handlerattr empty_attr = UPB_HANDLERATTR_INITIALIZER; + + UPB_UNUSED(closure); + + switch (upb_fielddef_type(key_field)) { + case UPB_TYPE_INT32: + upb_handlers_setint32(h, key_field, putmapkey_int32_t, &empty_attr); + break; + case UPB_TYPE_INT64: + upb_handlers_setint64(h, key_field, putmapkey_int64_t, &empty_attr); + break; + case UPB_TYPE_UINT32: + upb_handlers_setuint32(h, key_field, putmapkey_uint32_t, &empty_attr); + break; + case UPB_TYPE_UINT64: + upb_handlers_setuint64(h, key_field, putmapkey_uint64_t, &empty_attr); + break; + case UPB_TYPE_BOOL: + upb_handlers_setbool(h, key_field, putmapkey_bool, &empty_attr); + break; + case UPB_TYPE_STRING: + upb_handlers_setstartstr(h, key_field, mapkeyval_startstr, &empty_attr); + upb_handlers_setstring(h, key_field, mapkey_str, &empty_attr); + upb_handlers_setendstr(h, key_field, mapkey_endstr, &empty_attr); + break; + case UPB_TYPE_BYTES: + upb_handlers_setstring(h, key_field, mapkey_bytes, &empty_attr); + break; + default: + assert(false); + break; + } + + switch (upb_fielddef_type(value_field)) { + case UPB_TYPE_INT32: + upb_handlers_setint32(h, value_field, putint32_t, &empty_attr); + break; + case UPB_TYPE_INT64: + upb_handlers_setint64(h, value_field, putint64_t, &empty_attr); + break; + case UPB_TYPE_UINT32: + upb_handlers_setuint32(h, value_field, putuint32_t, &empty_attr); + break; + case UPB_TYPE_UINT64: + upb_handlers_setuint64(h, value_field, putuint64_t, &empty_attr); + break; + case UPB_TYPE_BOOL: + upb_handlers_setbool(h, value_field, putbool, &empty_attr); + break; + case UPB_TYPE_FLOAT: + upb_handlers_setfloat(h, value_field, putfloat, &empty_attr); + break; + case UPB_TYPE_DOUBLE: + upb_handlers_setdouble(h, value_field, putdouble, &empty_attr); + break; + case UPB_TYPE_STRING: + upb_handlers_setstartstr(h, value_field, mapkeyval_startstr, &empty_attr); + upb_handlers_setstring(h, value_field, putstr, &empty_attr); + upb_handlers_setendstr(h, value_field, mapvalue_endstr, &empty_attr); + break; + case UPB_TYPE_BYTES: + upb_handlers_setstring(h, value_field, putbytes, &empty_attr); + break; + case UPB_TYPE_ENUM: { + upb_handlerattr enum_attr = UPB_HANDLERATTR_INITIALIZER; + set_enum_hd(h, value_field, &enum_attr); + upb_handlers_setint32(h, value_field, mapvalue_enum, &enum_attr); + upb_handlerattr_uninit(&enum_attr); + break; + } + case UPB_TYPE_MESSAGE: + /* No handler necessary -- the submsg handlers will print the message + * as appropriate. */ + break; + } + + upb_handlerattr_uninit(&empty_attr); +} + +void printer_sethandlers(const void *closure, upb_handlers *h) { + const upb_msgdef *md = upb_handlers_msgdef(h); + bool is_mapentry = upb_msgdef_mapentry(md); + upb_handlerattr empty_attr = UPB_HANDLERATTR_INITIALIZER; + upb_msg_field_iter i; + + UPB_UNUSED(closure); + + if (is_mapentry) { + /* mapentry messages are sufficiently different that we handle them + * separately. */ + printer_sethandlers_mapentry(closure, h); + return; + } + + upb_handlers_setstartmsg(h, printer_startmsg, &empty_attr); + upb_handlers_setendmsg(h, printer_endmsg, &empty_attr); + +#define TYPE(type, name, ctype) \ + case type: \ + if (upb_fielddef_isseq(f)) { \ + upb_handlers_set##name(h, f, repeated_##ctype, &empty_attr); \ + } else { \ + upb_handlers_set##name(h, f, scalar_##ctype, &name_attr); \ + } \ + break; + + upb_msg_field_begin(&i, md); + for(; !upb_msg_field_done(&i); upb_msg_field_next(&i)) { + const upb_fielddef *f = upb_msg_iter_field(&i); + + upb_handlerattr name_attr = UPB_HANDLERATTR_INITIALIZER; + upb_handlerattr_sethandlerdata(&name_attr, newstrpc(h, f)); + + if (upb_fielddef_ismap(f)) { + upb_handlers_setstartseq(h, f, startmap, &name_attr); + upb_handlers_setendseq(h, f, endmap, &name_attr); + } else if (upb_fielddef_isseq(f)) { + upb_handlers_setstartseq(h, f, startseq, &name_attr); + upb_handlers_setendseq(h, f, endseq, &empty_attr); + } + + switch (upb_fielddef_type(f)) { + TYPE(UPB_TYPE_FLOAT, float, float); + TYPE(UPB_TYPE_DOUBLE, double, double); + TYPE(UPB_TYPE_BOOL, bool, bool); + TYPE(UPB_TYPE_INT32, int32, int32_t); + TYPE(UPB_TYPE_UINT32, uint32, uint32_t); + TYPE(UPB_TYPE_INT64, int64, int64_t); + TYPE(UPB_TYPE_UINT64, uint64, uint64_t); + case UPB_TYPE_ENUM: { + /* For now, we always emit symbolic names for enums. We may want an + * option later to control this behavior, but we will wait for a real + * need first. */ + upb_handlerattr enum_attr = UPB_HANDLERATTR_INITIALIZER; + set_enum_hd(h, f, &enum_attr); + + if (upb_fielddef_isseq(f)) { + upb_handlers_setint32(h, f, repeated_enum, &enum_attr); + } else { + upb_handlers_setint32(h, f, scalar_enum, &enum_attr); + } + + upb_handlerattr_uninit(&enum_attr); + break; + } + case UPB_TYPE_STRING: + if (upb_fielddef_isseq(f)) { + upb_handlers_setstartstr(h, f, repeated_startstr, &empty_attr); + upb_handlers_setstring(h, f, repeated_str, &empty_attr); + upb_handlers_setendstr(h, f, repeated_endstr, &empty_attr); + } else { + upb_handlers_setstartstr(h, f, scalar_startstr, &name_attr); + upb_handlers_setstring(h, f, scalar_str, &empty_attr); + upb_handlers_setendstr(h, f, scalar_endstr, &empty_attr); + } + break; + case UPB_TYPE_BYTES: + /* XXX: this doesn't support strings that span buffers yet. The base64 + * encoder will need to be made resumable for this to work properly. */ + if (upb_fielddef_isseq(f)) { + upb_handlers_setstring(h, f, repeated_bytes, &empty_attr); + } else { + upb_handlers_setstring(h, f, scalar_bytes, &name_attr); + } + break; + case UPB_TYPE_MESSAGE: + if (upb_fielddef_isseq(f)) { + upb_handlers_setstartsubmsg(h, f, repeated_startsubmsg, &name_attr); + } else { + upb_handlers_setstartsubmsg(h, f, scalar_startsubmsg, &name_attr); + } + break; + } + + upb_handlerattr_uninit(&name_attr); + } + + upb_handlerattr_uninit(&empty_attr); +#undef TYPE +} + +static void json_printer_reset(upb_json_printer *p) { + p->depth_ = 0; +} + + +/* Public API *****************************************************************/ + +upb_json_printer *upb_json_printer_create(upb_env *e, const upb_handlers *h, + upb_bytessink *output) { +#ifndef NDEBUG + size_t size_before = upb_env_bytesallocated(e); +#endif + + upb_json_printer *p = upb_env_malloc(e, sizeof(upb_json_printer)); + if (!p) return NULL; + + p->output_ = output; + json_printer_reset(p); + upb_sink_reset(&p->input_, h, p); + + /* If this fails, increase the value in printer.h. */ + assert(upb_env_bytesallocated(e) - size_before <= UPB_JSON_PRINTER_SIZE); + return p; +} + +upb_sink *upb_json_printer_input(upb_json_printer *p) { + return &p->input_; +} + +const upb_handlers *upb_json_printer_newhandlers(const upb_msgdef *md, + const void *owner) { + return upb_handlers_newfrozen(md, owner, printer_sethandlers, NULL); +} diff --git a/php/ext/google/protobuf/upb.h b/php/ext/google/protobuf/upb.h new file mode 100644 index 000000000..078e2a281 --- /dev/null +++ b/php/ext/google/protobuf/upb.h @@ -0,0 +1,8217 @@ +// Amalgamated source file +/* +** Defs are upb's internal representation of the constructs that can appear +** in a .proto file: +** +** - upb::MessageDef (upb_msgdef): describes a "message" construct. +** - upb::FieldDef (upb_fielddef): describes a message field. +** - upb::EnumDef (upb_enumdef): describes an enum. +** - upb::OneofDef (upb_oneofdef): describes a oneof. +** - upb::Def (upb_def): base class of all the others. +** +** TODO: definitions of services. +** +** Like upb_refcounted objects, defs are mutable only until frozen, and are +** only thread-safe once frozen. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ + +#ifndef UPB_DEF_H_ +#define UPB_DEF_H_ + +/* +** upb::RefCounted (upb_refcounted) +** +** A refcounting scheme that supports circular refs. It accomplishes this by +** partitioning the set of objects into groups such that no cycle spans groups; +** we can then reference-count the group as a whole and ignore refs within the +** group. When objects are mutable, these groups are computed very +** conservatively; we group any objects that have ever had a link between them. +** When objects are frozen, we compute strongly-connected components which +** allows us to be precise and only group objects that are actually cyclic. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ + +#ifndef UPB_REFCOUNTED_H_ +#define UPB_REFCOUNTED_H_ + +/* +** upb_table +** +** This header is INTERNAL-ONLY! Its interfaces are not public or stable! +** This file defines very fast int->upb_value (inttable) and string->upb_value +** (strtable) hash tables. +** +** The table uses chained scatter with Brent's variation (inspired by the Lua +** implementation of hash tables). The hash function for strings is Austin +** Appleby's "MurmurHash." +** +** The inttable uses uintptr_t as its key, which guarantees it can be used to +** store pointers or integers of at least 32 bits (upb isn't really useful on +** systems where sizeof(void*) < 4). +** +** The table must be homogenous (all values of the same type). In debug +** mode, we check this on insert and lookup. +*/ + +#ifndef UPB_TABLE_H_ +#define UPB_TABLE_H_ + +#include +#include +#include +/* +** This file contains shared definitions that are widely used across upb. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ + +#ifndef UPB_H_ +#define UPB_H_ + +#include +#include +#include +#include + +/* UPB_INLINE: inline if possible, emit standalone code if required. */ +#ifdef __cplusplus +#define UPB_INLINE inline +#elif defined (__GNUC__) +#define UPB_INLINE static __inline__ +#else +#define UPB_INLINE static +#endif + +/* Define UPB_BIG_ENDIAN manually if you're on big endian and your compiler + * doesn't provide these preprocessor symbols. */ +#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define UPB_BIG_ENDIAN +#endif + +/* Macros for function attributes on compilers that support them. */ +#ifdef __GNUC__ +#define UPB_FORCEINLINE __inline__ __attribute__((always_inline)) +#define UPB_NOINLINE __attribute__((noinline)) +#define UPB_NORETURN __attribute__((__noreturn__)) +#else /* !defined(__GNUC__) */ +#define UPB_FORCEINLINE +#define UPB_NOINLINE +#define UPB_NORETURN +#endif + +/* A few hacky workarounds for functions not in C89. + * For internal use only! + * TODO(haberman): fix these by including our own implementations, or finding + * another workaround. + */ +#ifdef __GNUC__ +#define _upb_snprintf __builtin_snprintf +#define _upb_vsnprintf __builtin_vsnprintf +#define _upb_va_copy(a, b) __va_copy(a, b) +#elif __STDC_VERSION__ >= 199901L +/* C99 versions. */ +#define _upb_snprintf snprintf +#define _upb_vsnprintf vsnprintf +#define _upb_va_copy(a, b) va_copy(a, b) +#else +#error Need implementations of [v]snprintf and va_copy +#endif + + +#if ((defined(__cplusplus) && __cplusplus >= 201103L) || \ + defined(__GXX_EXPERIMENTAL_CXX0X__)) && !defined(UPB_NO_CXX11) +#define UPB_CXX11 +#endif + +/* UPB_DISALLOW_COPY_AND_ASSIGN() + * UPB_DISALLOW_POD_OPS() + * + * Declare these in the "private" section of a C++ class to forbid copy/assign + * or all POD ops (construct, destruct, copy, assign) on that class. */ +#ifdef UPB_CXX11 +#include +#define UPB_DISALLOW_COPY_AND_ASSIGN(class_name) \ + class_name(const class_name&) = delete; \ + void operator=(const class_name&) = delete; +#define UPB_DISALLOW_POD_OPS(class_name, full_class_name) \ + class_name() = delete; \ + ~class_name() = delete; \ + UPB_DISALLOW_COPY_AND_ASSIGN(class_name) +#define UPB_ASSERT_STDLAYOUT(type) \ + static_assert(std::is_standard_layout::value, \ + #type " must be standard layout"); +#else /* !defined(UPB_CXX11) */ +#define UPB_DISALLOW_COPY_AND_ASSIGN(class_name) \ + class_name(const class_name&); \ + void operator=(const class_name&); +#define UPB_DISALLOW_POD_OPS(class_name, full_class_name) \ + class_name(); \ + ~class_name(); \ + UPB_DISALLOW_COPY_AND_ASSIGN(class_name) +#define UPB_ASSERT_STDLAYOUT(type) +#endif + +/* UPB_DECLARE_TYPE() + * UPB_DECLARE_DERIVED_TYPE() + * UPB_DECLARE_DERIVED_TYPE2() + * + * Macros for declaring C and C++ types both, including inheritance. + * The inheritance doesn't use real C++ inheritance, to stay compatible with C. + * + * These macros also provide upcasts: + * - in C: types-specific functions (ie. upb_foo_upcast(foo)) + * - in C++: upb::upcast(foo) along with implicit conversions + * + * Downcasts are not provided, but upb/def.h defines downcasts for upb::Def. */ + +#define UPB_C_UPCASTS(ty, base) \ + UPB_INLINE base *ty ## _upcast_mutable(ty *p) { return (base*)p; } \ + UPB_INLINE const base *ty ## _upcast(const ty *p) { return (const base*)p; } + +#define UPB_C_UPCASTS2(ty, base, base2) \ + UPB_C_UPCASTS(ty, base) \ + UPB_INLINE base2 *ty ## _upcast2_mutable(ty *p) { return (base2*)p; } \ + UPB_INLINE const base2 *ty ## _upcast2(const ty *p) { return (const base2*)p; } + +#ifdef __cplusplus + +#define UPB_BEGIN_EXTERN_C extern "C" { +#define UPB_END_EXTERN_C } +#define UPB_PRIVATE_FOR_CPP private: +#define UPB_DECLARE_TYPE(cppname, cname) typedef cppname cname; + +#define UPB_DECLARE_DERIVED_TYPE(cppname, cppbase, cname, cbase) \ + UPB_DECLARE_TYPE(cppname, cname) \ + UPB_C_UPCASTS(cname, cbase) \ + namespace upb { \ + template <> \ + class Pointer : public PointerBase { \ + public: \ + explicit Pointer(cppname* ptr) : PointerBase(ptr) {} \ + }; \ + template <> \ + class Pointer \ + : public PointerBase { \ + public: \ + explicit Pointer(const cppname* ptr) : PointerBase(ptr) {} \ + }; \ + } + +#define UPB_DECLARE_DERIVED_TYPE2(cppname, cppbase, cppbase2, cname, cbase, \ + cbase2) \ + UPB_DECLARE_TYPE(cppname, cname) \ + UPB_C_UPCASTS2(cname, cbase, cbase2) \ + namespace upb { \ + template <> \ + class Pointer : public PointerBase2 { \ + public: \ + explicit Pointer(cppname* ptr) : PointerBase2(ptr) {} \ + }; \ + template <> \ + class Pointer \ + : public PointerBase2 { \ + public: \ + explicit Pointer(const cppname* ptr) : PointerBase2(ptr) {} \ + }; \ + } + +#else /* !defined(__cplusplus) */ + +#define UPB_BEGIN_EXTERN_C +#define UPB_END_EXTERN_C +#define UPB_PRIVATE_FOR_CPP +#define UPB_DECLARE_TYPE(cppname, cname) \ + struct cname; \ + typedef struct cname cname; +#define UPB_DECLARE_DERIVED_TYPE(cppname, cppbase, cname, cbase) \ + UPB_DECLARE_TYPE(cppname, cname) \ + UPB_C_UPCASTS(cname, cbase) +#define UPB_DECLARE_DERIVED_TYPE2(cppname, cppbase, cppbase2, \ + cname, cbase, cbase2) \ + UPB_DECLARE_TYPE(cppname, cname) \ + UPB_C_UPCASTS2(cname, cbase, cbase2) + +#endif /* defined(__cplusplus) */ + +#define UPB_MAX(x, y) ((x) > (y) ? (x) : (y)) +#define UPB_MIN(x, y) ((x) < (y) ? (x) : (y)) + +#define UPB_UNUSED(var) (void)var + +/* For asserting something about a variable when the variable is not used for + * anything else. This prevents "unused variable" warnings when compiling in + * debug mode. */ +#define UPB_ASSERT_VAR(var, predicate) UPB_UNUSED(var); assert(predicate) + +/* Generic function type. */ +typedef void upb_func(); + +/* C++ Casts ******************************************************************/ + +#ifdef __cplusplus + +namespace upb { + +template class Pointer; + +/* Casts to a subclass. The caller must know that cast is correct; an + * incorrect cast will throw an assertion failure in debug mode. + * + * Example: + * upb::Def* def = GetDef(); + * // Assert-fails if this was not actually a MessageDef. + * upb::MessgeDef* md = upb::down_cast(def); + * + * Note that downcasts are only defined for some types (at the moment you can + * only downcast from a upb::Def to a specific Def type). */ +template To down_cast(From* f); + +/* Casts to a subclass. If the class does not actually match the given To type, + * returns NULL. + * + * Example: + * upb::Def* def = GetDef(); + * // md will be NULL if this was not actually a MessageDef. + * upb::MessgeDef* md = upb::down_cast(def); + * + * Note that dynamic casts are only defined for some types (at the moment you + * can only downcast from a upb::Def to a specific Def type).. */ +template To dyn_cast(From* f); + +/* Casts to any base class, or the type itself (ie. can be a no-op). + * + * Example: + * upb::MessageDef* md = GetDef(); + * // This will fail to compile if this wasn't actually a base class. + * upb::Def* def = upb::upcast(md); + */ +template inline Pointer upcast(T *f) { return Pointer(f); } + +/* Attempt upcast to specific base class. + * + * Example: + * upb::MessageDef* md = GetDef(); + * upb::upcast_to(md)->MethodOnDef(); + */ +template inline T* upcast_to(F *f) { + return static_cast(upcast(f)); +} + +/* PointerBase: implementation detail of upb::upcast(). + * It is implicitly convertable to pointers to the Base class(es). + */ +template +class PointerBase { + public: + explicit PointerBase(T* ptr) : ptr_(ptr) {} + operator T*() { return ptr_; } + operator Base*() { return (Base*)ptr_; } + + private: + T* ptr_; +}; + +template +class PointerBase2 : public PointerBase { + public: + explicit PointerBase2(T* ptr) : PointerBase(ptr) {} + operator Base2*() { return Pointer(*this); } +}; + +} + +#endif + + +/* upb::reffed_ptr ************************************************************/ + +#ifdef __cplusplus + +#include /* For std::swap(). */ + +namespace upb { + +/* Provides RAII semantics for upb refcounted objects. Each reffed_ptr owns a + * ref on whatever object it points to (if any). */ +template class reffed_ptr { + public: + reffed_ptr() : ptr_(NULL) {} + + /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ + template + reffed_ptr(U* val, const void* ref_donor = NULL) + : ptr_(upb::upcast(val)) { + if (ref_donor) { + assert(ptr_); + ptr_->DonateRef(ref_donor, this); + } else if (ptr_) { + ptr_->Ref(this); + } + } + + template + reffed_ptr(const reffed_ptr& other) + : ptr_(upb::upcast(other.get())) { + if (ptr_) ptr_->Ref(this); + } + + ~reffed_ptr() { if (ptr_) ptr_->Unref(this); } + + template + reffed_ptr& operator=(const reffed_ptr& other) { + reset(other.get()); + return *this; + } + + reffed_ptr& operator=(const reffed_ptr& other) { + reset(other.get()); + return *this; + } + + /* TODO(haberman): add C++11 move construction/assignment for greater + * efficiency. */ + + void swap(reffed_ptr& other) { + if (ptr_ == other.ptr_) { + return; + } + + if (ptr_) ptr_->DonateRef(this, &other); + if (other.ptr_) other.ptr_->DonateRef(&other, this); + std::swap(ptr_, other.ptr_); + } + + T& operator*() const { + assert(ptr_); + return *ptr_; + } + + T* operator->() const { + assert(ptr_); + return ptr_; + } + + T* get() const { return ptr_; } + + /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ + template + void reset(U* ptr = NULL, const void* ref_donor = NULL) { + reffed_ptr(ptr, ref_donor).swap(*this); + } + + template + reffed_ptr down_cast() { + return reffed_ptr(upb::down_cast(get())); + } + + template + reffed_ptr dyn_cast() { + return reffed_ptr(upb::dyn_cast(get())); + } + + /* Plain release() is unsafe; if we were the only owner, it would leak the + * object. Instead we provide this: */ + T* ReleaseTo(const void* new_owner) { + T* ret = NULL; + ptr_->DonateRef(this, new_owner); + std::swap(ret, ptr_); + return ret; + } + + private: + T* ptr_; +}; + +} /* namespace upb */ + +#endif /* __cplusplus */ + + +/* upb::Status ****************************************************************/ + +#ifdef __cplusplus +namespace upb { +class ErrorSpace; +class Status; +} +#endif + +UPB_DECLARE_TYPE(upb::ErrorSpace, upb_errorspace) +UPB_DECLARE_TYPE(upb::Status, upb_status) + +/* The maximum length of an error message before it will get truncated. */ +#define UPB_STATUS_MAX_MESSAGE 128 + +/* An error callback function is used to report errors from some component. + * The function can return "true" to indicate that the component should try + * to recover and proceed, but this is not always possible. */ +typedef bool upb_errcb_t(void *closure, const upb_status* status); + +#ifdef __cplusplus +class upb::ErrorSpace { +#else +struct upb_errorspace { +#endif + const char *name; + /* Should the error message in the status object according to this code. */ + void (*set_message)(upb_status* status, int code); +}; + +#ifdef __cplusplus + +/* Object representing a success or failure status. + * It owns no resources and allocates no memory, so it should work + * even in OOM situations. */ + +class upb::Status { + public: + Status(); + + /* Returns true if there is no error. */ + bool ok() const; + + /* Optional error space and code, useful if the caller wants to + * programmatically check the specific kind of error. */ + ErrorSpace* error_space(); + int code() const; + + const char *error_message() const; + + /* The error message will be truncated if it is longer than + * UPB_STATUS_MAX_MESSAGE-4. */ + void SetErrorMessage(const char* msg); + void SetFormattedErrorMessage(const char* fmt, ...); + + /* If there is no error message already, this will use the ErrorSpace to + * populate the error message for this code. The caller can still call + * SetErrorMessage() to give a more specific message. */ + void SetErrorCode(ErrorSpace* space, int code); + + /* Resets the status to a successful state with no message. */ + void Clear(); + + void CopyFrom(const Status& other); + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(Status) +#else +struct upb_status { +#endif + bool ok_; + + /* Specific status code defined by some error space (optional). */ + int code_; + upb_errorspace *error_space_; + + /* Error message; NULL-terminated. */ + char msg[UPB_STATUS_MAX_MESSAGE]; +}; + +#define UPB_STATUS_INIT {true, 0, NULL, {0}} + +#ifdef __cplusplus +extern "C" { +#endif + +/* The returned string is invalidated by any other call into the status. */ +const char *upb_status_errmsg(const upb_status *status); +bool upb_ok(const upb_status *status); +upb_errorspace *upb_status_errspace(const upb_status *status); +int upb_status_errcode(const upb_status *status); + +/* Any of the functions that write to a status object allow status to be NULL, + * to support use cases where the function's caller does not care about the + * status message. */ +void upb_status_clear(upb_status *status); +void upb_status_seterrmsg(upb_status *status, const char *msg); +void upb_status_seterrf(upb_status *status, const char *fmt, ...); +void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args); +void upb_status_seterrcode(upb_status *status, upb_errorspace *space, int code); +void upb_status_copy(upb_status *to, const upb_status *from); + +#ifdef __cplusplus +} /* extern "C" */ + +namespace upb { + +/* C++ Wrappers */ +inline Status::Status() { Clear(); } +inline bool Status::ok() const { return upb_ok(this); } +inline const char* Status::error_message() const { + return upb_status_errmsg(this); +} +inline void Status::SetErrorMessage(const char* msg) { + upb_status_seterrmsg(this, msg); +} +inline void Status::SetFormattedErrorMessage(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + upb_status_vseterrf(this, fmt, args); + va_end(args); +} +inline void Status::SetErrorCode(ErrorSpace* space, int code) { + upb_status_seterrcode(this, space, code); +} +inline void Status::Clear() { upb_status_clear(this); } +inline void Status::CopyFrom(const Status& other) { + upb_status_copy(this, &other); +} + +} /* namespace upb */ + +#endif + +#endif /* UPB_H_ */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* upb_value ******************************************************************/ + +/* A tagged union (stored untagged inside the table) so that we can check that + * clients calling table accessors are correctly typed without having to have + * an explosion of accessors. */ +typedef enum { + UPB_CTYPE_INT32 = 1, + UPB_CTYPE_INT64 = 2, + UPB_CTYPE_UINT32 = 3, + UPB_CTYPE_UINT64 = 4, + UPB_CTYPE_BOOL = 5, + UPB_CTYPE_CSTR = 6, + UPB_CTYPE_PTR = 7, + UPB_CTYPE_CONSTPTR = 8, + UPB_CTYPE_FPTR = 9 +} upb_ctype_t; + +typedef struct { + uint64_t val; +#ifndef NDEBUG + /* In debug mode we carry the value type around also so we can check accesses + * to be sure the right member is being read. */ + upb_ctype_t ctype; +#endif +} upb_value; + +#ifdef NDEBUG +#define SET_TYPE(dest, val) UPB_UNUSED(val) +#else +#define SET_TYPE(dest, val) dest = val +#endif + +/* Like strdup(), which isn't always available since it's not ANSI C. */ +char *upb_strdup(const char *s); +/* Variant that works with a length-delimited rather than NULL-delimited string, + * as supported by strtable. */ +char *upb_strdup2(const char *s, size_t len); + +UPB_INLINE void _upb_value_setval(upb_value *v, uint64_t val, + upb_ctype_t ctype) { + v->val = val; + SET_TYPE(v->ctype, ctype); +} + +UPB_INLINE upb_value _upb_value_val(uint64_t val, upb_ctype_t ctype) { + upb_value ret; + _upb_value_setval(&ret, val, ctype); + return ret; +} + +/* For each value ctype, define the following set of functions: + * + * // Get/set an int32 from a upb_value. + * int32_t upb_value_getint32(upb_value val); + * void upb_value_setint32(upb_value *val, int32_t cval); + * + * // Construct a new upb_value from an int32. + * upb_value upb_value_int32(int32_t val); */ +#define FUNCS(name, membername, type_t, converter, proto_type) \ + UPB_INLINE void upb_value_set ## name(upb_value *val, type_t cval) { \ + val->val = (converter)cval; \ + SET_TYPE(val->ctype, proto_type); \ + } \ + UPB_INLINE upb_value upb_value_ ## name(type_t val) { \ + upb_value ret; \ + upb_value_set ## name(&ret, val); \ + return ret; \ + } \ + UPB_INLINE type_t upb_value_get ## name(upb_value val) { \ + assert(val.ctype == proto_type); \ + return (type_t)(converter)val.val; \ + } + +FUNCS(int32, int32, int32_t, int32_t, UPB_CTYPE_INT32) +FUNCS(int64, int64, int64_t, int64_t, UPB_CTYPE_INT64) +FUNCS(uint32, uint32, uint32_t, uint32_t, UPB_CTYPE_UINT32) +FUNCS(uint64, uint64, uint64_t, uint64_t, UPB_CTYPE_UINT64) +FUNCS(bool, _bool, bool, bool, UPB_CTYPE_BOOL) +FUNCS(cstr, cstr, char*, uintptr_t, UPB_CTYPE_CSTR) +FUNCS(ptr, ptr, void*, uintptr_t, UPB_CTYPE_PTR) +FUNCS(constptr, constptr, const void*, uintptr_t, UPB_CTYPE_CONSTPTR) +FUNCS(fptr, fptr, upb_func*, uintptr_t, UPB_CTYPE_FPTR) + +#undef FUNCS +#undef SET_TYPE + + +/* upb_tabkey *****************************************************************/ + +/* Either: + * 1. an actual integer key, or + * 2. a pointer to a string prefixed by its uint32_t length, owned by us. + * + * ...depending on whether this is a string table or an int table. We would + * make this a union of those two types, but C89 doesn't support statically + * initializing a non-first union member. */ +typedef uintptr_t upb_tabkey; + +#define UPB_TABKEY_NUM(n) n +#define UPB_TABKEY_NONE 0 +/* The preprocessor isn't quite powerful enough to turn the compile-time string + * length into a byte-wise string representation, so code generation needs to + * help it along. + * + * "len1" is the low byte and len4 is the high byte. */ +#ifdef UPB_BIG_ENDIAN +#define UPB_TABKEY_STR(len1, len2, len3, len4, strval) \ + (uintptr_t)(len4 len3 len2 len1 strval) +#else +#define UPB_TABKEY_STR(len1, len2, len3, len4, strval) \ + (uintptr_t)(len1 len2 len3 len4 strval) +#endif + +UPB_INLINE char *upb_tabstr(upb_tabkey key, uint32_t *len) { + char* mem = (char*)key; + if (len) memcpy(len, mem, sizeof(*len)); + return mem + sizeof(*len); +} + + +/* upb_tabval *****************************************************************/ + +#ifdef __cplusplus + +/* Status initialization not supported. + * + * This separate definition is necessary because in C++, UINTPTR_MAX isn't + * reliably available. */ +typedef struct { + uint64_t val; +} upb_tabval; + +#else + +/* C -- supports static initialization, but to support static initialization of + * both integers and points for both 32 and 64 bit targets, it takes a little + * bit of doing. */ + +#if UINTPTR_MAX == 0xffffffffffffffffULL +#define UPB_PTR_IS_64BITS +#elif UINTPTR_MAX != 0xffffffff +#error Could not determine how many bits pointers are. +#endif + +typedef union { + /* For static initialization. + * + * Unfortunately this ugliness is necessary -- it is the only way that we can, + * with -std=c89 -pedantic, statically initialize this to either a pointer or + * an integer on 32-bit platforms. */ + struct { +#ifdef UPB_PTR_IS_64BITS + uintptr_t val; +#else + uintptr_t val1; + uintptr_t val2; +#endif + } staticinit; + + /* The normal accessor that we use for everything at runtime. */ + uint64_t val; +} upb_tabval; + +#ifdef UPB_PTR_IS_64BITS +#define UPB_TABVALUE_INT_INIT(v) {{v}} +#define UPB_TABVALUE_EMPTY_INIT {{-1}} +#else + +/* 32-bit pointers */ + +#ifdef UPB_BIG_ENDIAN +#define UPB_TABVALUE_INT_INIT(v) {{0, v}} +#define UPB_TABVALUE_EMPTY_INIT {{-1, -1}} +#else +#define UPB_TABVALUE_INT_INIT(v) {{v, 0}} +#define UPB_TABVALUE_EMPTY_INIT {{-1, -1}} +#endif + +#endif + +#define UPB_TABVALUE_PTR_INIT(v) UPB_TABVALUE_INT_INIT((uintptr_t)v) + +#undef UPB_PTR_IS_64BITS + +#endif /* __cplusplus */ + + +/* upb_table ******************************************************************/ + +typedef struct _upb_tabent { + upb_tabkey key; + upb_tabval val; + + /* Internal chaining. This is const so we can create static initializers for + * tables. We cast away const sometimes, but *only* when the containing + * upb_table is known to be non-const. This requires a bit of care, but + * the subtlety is confined to table.c. */ + const struct _upb_tabent *next; +} upb_tabent; + +typedef struct { + size_t count; /* Number of entries in the hash part. */ + size_t mask; /* Mask to turn hash value -> bucket. */ + upb_ctype_t ctype; /* Type of all values. */ + uint8_t size_lg2; /* Size of the hashtable part is 2^size_lg2 entries. */ + + /* Hash table entries. + * Making this const isn't entirely accurate; what we really want is for it to + * have the same const-ness as the table it's inside. But there's no way to + * declare that in C. So we have to make it const so that we can statically + * initialize const hash tables. Then we cast away const when we have to. + */ + const upb_tabent *entries; +} upb_table; + +typedef struct { + upb_table t; +} upb_strtable; + +#define UPB_STRTABLE_INIT(count, mask, ctype, size_lg2, entries) \ + {{count, mask, ctype, size_lg2, entries}} + +#define UPB_EMPTY_STRTABLE_INIT(ctype) \ + UPB_STRTABLE_INIT(0, 0, ctype, 0, NULL) + +typedef struct { + upb_table t; /* For entries that don't fit in the array part. */ + const upb_tabval *array; /* Array part of the table. See const note above. */ + size_t array_size; /* Array part size. */ + size_t array_count; /* Array part number of elements. */ +} upb_inttable; + +#define UPB_INTTABLE_INIT(count, mask, ctype, size_lg2, ent, a, asize, acount) \ + {{count, mask, ctype, size_lg2, ent}, a, asize, acount} + +#define UPB_EMPTY_INTTABLE_INIT(ctype) \ + UPB_INTTABLE_INIT(0, 0, ctype, 0, NULL, NULL, 0, 0) + +#define UPB_ARRAY_EMPTYENT -1 + +UPB_INLINE size_t upb_table_size(const upb_table *t) { + if (t->size_lg2 == 0) + return 0; + else + return 1 << t->size_lg2; +} + +/* Internal-only functions, in .h file only out of necessity. */ +UPB_INLINE bool upb_tabent_isempty(const upb_tabent *e) { + return e->key == 0; +} + +/* Used by some of the unit tests for generic hashing functionality. */ +uint32_t MurmurHash2(const void * key, size_t len, uint32_t seed); + +UPB_INLINE uintptr_t upb_intkey(uintptr_t key) { + return key; +} + +UPB_INLINE uint32_t upb_inthash(uintptr_t key) { + return (uint32_t)key; +} + +static const upb_tabent *upb_getentry(const upb_table *t, uint32_t hash) { + return t->entries + (hash & t->mask); +} + +UPB_INLINE bool upb_arrhas(upb_tabval key) { + return key.val != (uint64_t)-1; +} + +/* Initialize and uninitialize a table, respectively. If memory allocation + * failed, false is returned that the table is uninitialized. */ +bool upb_inttable_init(upb_inttable *table, upb_ctype_t ctype); +bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype); +void upb_inttable_uninit(upb_inttable *table); +void upb_strtable_uninit(upb_strtable *table); + +/* Returns the number of values in the table. */ +size_t upb_inttable_count(const upb_inttable *t); +UPB_INLINE size_t upb_strtable_count(const upb_strtable *t) { + return t->t.count; +} + +/* Inserts the given key into the hashtable with the given value. The key must + * not already exist in the hash table. For string tables, the key must be + * NULL-terminated, and the table will make an internal copy of the key. + * Inttables must not insert a value of UINTPTR_MAX. + * + * If a table resize was required but memory allocation failed, false is + * returned and the table is unchanged. */ +bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val); +bool upb_strtable_insert2(upb_strtable *t, const char *key, size_t len, + upb_value val); + +/* For NULL-terminated strings. */ +UPB_INLINE bool upb_strtable_insert(upb_strtable *t, const char *key, + upb_value val) { + return upb_strtable_insert2(t, key, strlen(key), val); +} + +/* Looks up key in this table, returning "true" if the key was found. + * If v is non-NULL, copies the value for this key into *v. */ +bool upb_inttable_lookup(const upb_inttable *t, uintptr_t key, upb_value *v); +bool upb_strtable_lookup2(const upb_strtable *t, const char *key, size_t len, + upb_value *v); + +/* For NULL-terminated strings. */ +UPB_INLINE bool upb_strtable_lookup(const upb_strtable *t, const char *key, + upb_value *v) { + return upb_strtable_lookup2(t, key, strlen(key), v); +} + +/* Removes an item from the table. Returns true if the remove was successful, + * and stores the removed item in *val if non-NULL. */ +bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val); +bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len, + upb_value *val); + +/* For NULL-terminated strings. */ +UPB_INLINE bool upb_strtable_remove(upb_strtable *t, const char *key, + upb_value *v) { + return upb_strtable_remove2(t, key, strlen(key), v); +} + +/* Updates an existing entry in an inttable. If the entry does not exist, + * returns false and does nothing. Unlike insert/remove, this does not + * invalidate iterators. */ +bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val); + +/* Handy routines for treating an inttable like a stack. May not be mixed with + * other insert/remove calls. */ +bool upb_inttable_push(upb_inttable *t, upb_value val); +upb_value upb_inttable_pop(upb_inttable *t); + +/* Convenience routines for inttables with pointer keys. */ +bool upb_inttable_insertptr(upb_inttable *t, const void *key, upb_value val); +bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val); +bool upb_inttable_lookupptr( + const upb_inttable *t, const void *key, upb_value *val); + +/* Optimizes the table for the current set of entries, for both memory use and + * lookup time. Client should call this after all entries have been inserted; + * inserting more entries is legal, but will likely require a table resize. */ +void upb_inttable_compact(upb_inttable *t); + +/* A special-case inlinable version of the lookup routine for 32-bit + * integers. */ +UPB_INLINE bool upb_inttable_lookup32(const upb_inttable *t, uint32_t key, + upb_value *v) { + *v = upb_value_int32(0); /* Silence compiler warnings. */ + if (key < t->array_size) { + upb_tabval arrval = t->array[key]; + if (upb_arrhas(arrval)) { + _upb_value_setval(v, arrval.val, t->t.ctype); + return true; + } else { + return false; + } + } else { + const upb_tabent *e; + if (t->t.entries == NULL) return false; + for (e = upb_getentry(&t->t, upb_inthash(key)); true; e = e->next) { + if ((uint32_t)e->key == key) { + _upb_value_setval(v, e->val.val, t->t.ctype); + return true; + } + if (e->next == NULL) return false; + } + } +} + +/* Exposed for testing only. */ +bool upb_strtable_resize(upb_strtable *t, size_t size_lg2); + +/* Iterators ******************************************************************/ + +/* Iterators for int and string tables. We are subject to some kind of unusual + * design constraints: + * + * For high-level languages: + * - we must be able to guarantee that we don't crash or corrupt memory even if + * the program accesses an invalidated iterator. + * + * For C++11 range-based for: + * - iterators must be copyable + * - iterators must be comparable + * - it must be possible to construct an "end" value. + * + * Iteration order is undefined. + * + * Modifying the table invalidates iterators. upb_{str,int}table_done() is + * guaranteed to work even on an invalidated iterator, as long as the table it + * is iterating over has not been freed. Calling next() or accessing data from + * an invalidated iterator yields unspecified elements from the table, but it is + * guaranteed not to crash and to return real table elements (except when done() + * is true). */ + + +/* upb_strtable_iter **********************************************************/ + +/* upb_strtable_iter i; + * upb_strtable_begin(&i, t); + * for(; !upb_strtable_done(&i); upb_strtable_next(&i)) { + * const char *key = upb_strtable_iter_key(&i); + * const upb_value val = upb_strtable_iter_value(&i); + * // ... + * } + */ + +typedef struct { + const upb_strtable *t; + size_t index; +} upb_strtable_iter; + +void upb_strtable_begin(upb_strtable_iter *i, const upb_strtable *t); +void upb_strtable_next(upb_strtable_iter *i); +bool upb_strtable_done(const upb_strtable_iter *i); +const char *upb_strtable_iter_key(upb_strtable_iter *i); +size_t upb_strtable_iter_keylength(upb_strtable_iter *i); +upb_value upb_strtable_iter_value(const upb_strtable_iter *i); +void upb_strtable_iter_setdone(upb_strtable_iter *i); +bool upb_strtable_iter_isequal(const upb_strtable_iter *i1, + const upb_strtable_iter *i2); + + +/* upb_inttable_iter **********************************************************/ + +/* upb_inttable_iter i; + * upb_inttable_begin(&i, t); + * for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { + * uintptr_t key = upb_inttable_iter_key(&i); + * upb_value val = upb_inttable_iter_value(&i); + * // ... + * } + */ + +typedef struct { + const upb_inttable *t; + size_t index; + bool array_part; +} upb_inttable_iter; + +void upb_inttable_begin(upb_inttable_iter *i, const upb_inttable *t); +void upb_inttable_next(upb_inttable_iter *i); +bool upb_inttable_done(const upb_inttable_iter *i); +uintptr_t upb_inttable_iter_key(const upb_inttable_iter *i); +upb_value upb_inttable_iter_value(const upb_inttable_iter *i); +void upb_inttable_iter_setdone(upb_inttable_iter *i); +bool upb_inttable_iter_isequal(const upb_inttable_iter *i1, + const upb_inttable_iter *i2); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* UPB_TABLE_H_ */ + +/* Reference tracking will check ref()/unref() operations to make sure the + * ref ownership is correct. Where possible it will also make tools like + * Valgrind attribute ref leaks to the code that took the leaked ref, not + * the code that originally created the object. + * + * Enabling this requires the application to define upb_lock()/upb_unlock() + * functions that acquire/release a global mutex (or #define UPB_THREAD_UNSAFE). + * For this reason we don't enable it by default, even in debug builds. + */ + +/* #define UPB_DEBUG_REFS */ + +#ifdef __cplusplus +namespace upb { class RefCounted; } +#endif + +UPB_DECLARE_TYPE(upb::RefCounted, upb_refcounted) + +struct upb_refcounted_vtbl; + +#ifdef __cplusplus + +class upb::RefCounted { + public: + /* Returns true if the given object is frozen. */ + bool IsFrozen() const; + + /* Increases the ref count, the new ref is owned by "owner" which must not + * already own a ref (and should not itself be a refcounted object if the ref + * could possibly be circular; see below). + * Thread-safe iff "this" is frozen. */ + void Ref(const void *owner) const; + + /* Release a ref that was acquired from upb_refcounted_ref() and collects any + * objects it can. */ + void Unref(const void *owner) const; + + /* Moves an existing ref from "from" to "to", without changing the overall + * ref count. DonateRef(foo, NULL, owner) is the same as Ref(foo, owner), + * but "to" may not be NULL. */ + void DonateRef(const void *from, const void *to) const; + + /* Verifies that a ref to the given object is currently held by the given + * owner. Only effective in UPB_DEBUG_REFS builds. */ + void CheckRef(const void *owner) const; + + private: + UPB_DISALLOW_POD_OPS(RefCounted, upb::RefCounted) +#else +struct upb_refcounted { +#endif + /* TODO(haberman): move the actual structure definition to structdefs.int.h. + * The only reason they are here is because inline functions need to see the + * definition of upb_handlers, which needs to see this definition. But we + * can change the upb_handlers inline functions to deal in raw offsets + * instead. + */ + + /* A single reference count shared by all objects in the group. */ + uint32_t *group; + + /* A singly-linked list of all objects in the group. */ + upb_refcounted *next; + + /* Table of function pointers for this type. */ + const struct upb_refcounted_vtbl *vtbl; + + /* Maintained only when mutable, this tracks the number of refs (but not + * ref2's) to this object. *group should be the sum of all individual_count + * in the group. */ + uint32_t individual_count; + + bool is_frozen; + +#ifdef UPB_DEBUG_REFS + upb_inttable *refs; /* Maps owner -> trackedref for incoming refs. */ + upb_inttable *ref2s; /* Set of targets for outgoing ref2s. */ +#endif +}; + +#ifdef UPB_DEBUG_REFS +#define UPB_REFCOUNT_INIT(refs, ref2s) \ + {&static_refcount, NULL, NULL, 0, true, refs, ref2s} +#else +#define UPB_REFCOUNT_INIT(refs, ref2s) {&static_refcount, NULL, NULL, 0, true} +#endif + +UPB_BEGIN_EXTERN_C + +/* It is better to use tracked refs when possible, for the extra debugging + * capability. But if this is not possible (because you don't have easy access + * to a stable pointer value that is associated with the ref), you can pass + * UPB_UNTRACKED_REF instead. */ +extern const void *UPB_UNTRACKED_REF; + +/* Native C API. */ +bool upb_refcounted_isfrozen(const upb_refcounted *r); +void upb_refcounted_ref(const upb_refcounted *r, const void *owner); +void upb_refcounted_unref(const upb_refcounted *r, const void *owner); +void upb_refcounted_donateref( + const upb_refcounted *r, const void *from, const void *to); +void upb_refcounted_checkref(const upb_refcounted *r, const void *owner); + +#define UPB_REFCOUNTED_CMETHODS(type, upcastfunc) \ + UPB_INLINE bool type ## _isfrozen(const type *v) { \ + return upb_refcounted_isfrozen(upcastfunc(v)); \ + } \ + UPB_INLINE void type ## _ref(const type *v, const void *owner) { \ + upb_refcounted_ref(upcastfunc(v), owner); \ + } \ + UPB_INLINE void type ## _unref(const type *v, const void *owner) { \ + upb_refcounted_unref(upcastfunc(v), owner); \ + } \ + UPB_INLINE void type ## _donateref(const type *v, const void *from, const void *to) { \ + upb_refcounted_donateref(upcastfunc(v), from, to); \ + } \ + UPB_INLINE void type ## _checkref(const type *v, const void *owner) { \ + upb_refcounted_checkref(upcastfunc(v), owner); \ + } + +#define UPB_REFCOUNTED_CPPMETHODS \ + bool IsFrozen() const { \ + return upb::upcast_to(this)->IsFrozen(); \ + } \ + void Ref(const void *owner) const { \ + return upb::upcast_to(this)->Ref(owner); \ + } \ + void Unref(const void *owner) const { \ + return upb::upcast_to(this)->Unref(owner); \ + } \ + void DonateRef(const void *from, const void *to) const { \ + return upb::upcast_to(this)->DonateRef(from, to); \ + } \ + void CheckRef(const void *owner) const { \ + return upb::upcast_to(this)->CheckRef(owner); \ + } + +/* Internal-to-upb Interface **************************************************/ + +typedef void upb_refcounted_visit(const upb_refcounted *r, + const upb_refcounted *subobj, + void *closure); + +struct upb_refcounted_vtbl { + /* Must visit all subobjects that are currently ref'd via upb_refcounted_ref2. + * Must be longjmp()-safe. */ + void (*visit)(const upb_refcounted *r, upb_refcounted_visit *visit, void *c); + + /* Must free the object and release all references to other objects. */ + void (*free)(upb_refcounted *r); +}; + +/* Initializes the refcounted with a single ref for the given owner. Returns + * false if memory could not be allocated. */ +bool upb_refcounted_init(upb_refcounted *r, + const struct upb_refcounted_vtbl *vtbl, + const void *owner); + +/* Adds a ref from one refcounted object to another ("from" must not already + * own a ref). These refs may be circular; cycles will be collected correctly + * (if conservatively). These refs do not need to be freed in from's free() + * function. */ +void upb_refcounted_ref2(const upb_refcounted *r, upb_refcounted *from); + +/* Removes a ref that was acquired from upb_refcounted_ref2(), and collects any + * object it can. This is only necessary when "from" no longer points to "r", + * and not from from's "free" function. */ +void upb_refcounted_unref2(const upb_refcounted *r, upb_refcounted *from); + +#define upb_ref2(r, from) \ + upb_refcounted_ref2((const upb_refcounted*)r, (upb_refcounted*)from) +#define upb_unref2(r, from) \ + upb_refcounted_unref2((const upb_refcounted*)r, (upb_refcounted*)from) + +/* Freezes all mutable object reachable by ref2() refs from the given roots. + * This will split refcounting groups into precise SCC groups, so that + * refcounting of frozen objects can be more aggressive. If memory allocation + * fails, or if more than 2**31 mutable objects are reachable from "roots", or + * if the maximum depth of the graph exceeds "maxdepth", false is returned and + * the objects are unchanged. + * + * After this operation succeeds, the objects are frozen/const, and may not be + * used through non-const pointers. In particular, they may not be passed as + * the second parameter of upb_refcounted_{ref,unref}2(). On the upside, all + * operations on frozen refcounteds are threadsafe, and objects will be freed + * at the precise moment that they become unreachable. + * + * Caller must own refs on each object in the "roots" list. */ +bool upb_refcounted_freeze(upb_refcounted *const*roots, int n, upb_status *s, + int maxdepth); + +/* Shared by all compiled-in refcounted objects. */ +extern uint32_t static_refcount; + +UPB_END_EXTERN_C + +#ifdef __cplusplus +/* C++ Wrappers. */ +namespace upb { +inline bool RefCounted::IsFrozen() const { + return upb_refcounted_isfrozen(this); +} +inline void RefCounted::Ref(const void *owner) const { + upb_refcounted_ref(this, owner); +} +inline void RefCounted::Unref(const void *owner) const { + upb_refcounted_unref(this, owner); +} +inline void RefCounted::DonateRef(const void *from, const void *to) const { + upb_refcounted_donateref(this, from, to); +} +inline void RefCounted::CheckRef(const void *owner) const { + upb_refcounted_checkref(this, owner); +} +} /* namespace upb */ +#endif + +#endif /* UPB_REFCOUNT_H_ */ + +#ifdef __cplusplus +#include +#include +#include + +namespace upb { +class Def; +class EnumDef; +class FieldDef; +class MessageDef; +class OneofDef; +} +#endif + +UPB_DECLARE_DERIVED_TYPE(upb::Def, upb::RefCounted, upb_def, upb_refcounted) + +/* The maximum message depth that the type graph can have. This is a resource + * limit for the C stack since we sometimes need to recursively traverse the + * graph. Cycles are ok; the traversal will stop when it detects a cycle, but + * we must hit the cycle before the maximum depth is reached. + * + * If having a single static limit is too inflexible, we can add another variant + * of Def::Freeze that allows specifying this as a parameter. */ +#define UPB_MAX_MESSAGE_DEPTH 64 + + +/* upb::Def: base class for defs *********************************************/ + +/* All the different kind of defs we support. These correspond 1:1 with + * declarations in a .proto file. */ +typedef enum { + UPB_DEF_MSG, + UPB_DEF_FIELD, + UPB_DEF_ENUM, + UPB_DEF_ONEOF, + UPB_DEF_SERVICE, /* Not yet implemented. */ + UPB_DEF_ANY = -1 /* Wildcard for upb_symtab_get*() */ +} upb_deftype_t; + +#ifdef __cplusplus + +/* The base class of all defs. Its base is upb::RefCounted (use upb::upcast() + * to convert). */ +class upb::Def { + public: + typedef upb_deftype_t Type; + + Def* Dup(const void *owner) const; + + /* upb::RefCounted methods like Ref()/Unref(). */ + UPB_REFCOUNTED_CPPMETHODS + + Type def_type() const; + + /* "fullname" is the def's fully-qualified name (eg. foo.bar.Message). */ + const char *full_name() const; + + /* The def must be mutable. Caller retains ownership of fullname. Defs are + * not required to have a name; if a def has no name when it is frozen, it + * will remain an anonymous def. On failure, returns false and details in "s" + * if non-NULL. */ + bool set_full_name(const char* fullname, upb::Status* s); + bool set_full_name(const std::string &fullname, upb::Status* s); + + /* Freezes the given defs; this validates all constraints and marks the defs + * as frozen (read-only). "defs" may not contain any fielddefs, but fields + * of any msgdefs will be frozen. + * + * Symbolic references to sub-types and enum defaults must have already been + * resolved. Any mutable defs reachable from any of "defs" must also be in + * the list; more formally, "defs" must be a transitive closure of mutable + * defs. + * + * After this operation succeeds, the finalized defs must only be accessed + * through a const pointer! */ + static bool Freeze(Def* const* defs, int n, Status* status); + static bool Freeze(const std::vector& defs, Status* status); + + private: + UPB_DISALLOW_POD_OPS(Def, upb::Def) +}; + +#endif /* __cplusplus */ + +UPB_BEGIN_EXTERN_C + +/* Native C API. */ +upb_def *upb_def_dup(const upb_def *def, const void *owner); + +/* Include upb_refcounted methods like upb_def_ref()/upb_def_unref(). */ +UPB_REFCOUNTED_CMETHODS(upb_def, upb_def_upcast) + +upb_deftype_t upb_def_type(const upb_def *d); +const char *upb_def_fullname(const upb_def *d); +bool upb_def_setfullname(upb_def *def, const char *fullname, upb_status *s); +bool upb_def_freeze(upb_def *const *defs, int n, upb_status *s); + +UPB_END_EXTERN_C + + +/* upb::Def casts *************************************************************/ + +#ifdef __cplusplus +#define UPB_CPP_CASTS(cname, cpptype) \ + namespace upb { \ + template <> \ + inline cpptype *down_cast(Def * def) { \ + return upb_downcast_##cname##_mutable(def); \ + } \ + template <> \ + inline cpptype *dyn_cast(Def * def) { \ + return upb_dyncast_##cname##_mutable(def); \ + } \ + template <> \ + inline const cpptype *down_cast( \ + const Def *def) { \ + return upb_downcast_##cname(def); \ + } \ + template <> \ + inline const cpptype *dyn_cast(const Def *def) { \ + return upb_dyncast_##cname(def); \ + } \ + template <> \ + inline const cpptype *down_cast(Def * def) { \ + return upb_downcast_##cname(def); \ + } \ + template <> \ + inline const cpptype *dyn_cast(Def * def) { \ + return upb_dyncast_##cname(def); \ + } \ + } /* namespace upb */ +#else +#define UPB_CPP_CASTS(cname, cpptype) +#endif /* __cplusplus */ + +/* Dynamic casts, for determining if a def is of a particular type at runtime. + * Downcasts, for when some wants to assert that a def is of a particular type. + * These are only checked if we are building debug. */ +#define UPB_DEF_CASTS(lower, upper, cpptype) \ + UPB_INLINE const upb_##lower *upb_dyncast_##lower(const upb_def *def) { \ + if (upb_def_type(def) != UPB_DEF_##upper) return NULL; \ + return (upb_##lower *)def; \ + } \ + UPB_INLINE const upb_##lower *upb_downcast_##lower(const upb_def *def) { \ + assert(upb_def_type(def) == UPB_DEF_##upper); \ + return (const upb_##lower *)def; \ + } \ + UPB_INLINE upb_##lower *upb_dyncast_##lower##_mutable(upb_def *def) { \ + return (upb_##lower *)upb_dyncast_##lower(def); \ + } \ + UPB_INLINE upb_##lower *upb_downcast_##lower##_mutable(upb_def *def) { \ + return (upb_##lower *)upb_downcast_##lower(def); \ + } \ + UPB_CPP_CASTS(lower, cpptype) + +#define UPB_DEFINE_DEF(cppname, lower, upper, cppmethods, members) \ + UPB_DEFINE_CLASS2(cppname, upb::Def, upb::RefCounted, cppmethods, \ + members) \ + UPB_DEF_CASTS(lower, upper, cppname) + +#define UPB_DECLARE_DEF_TYPE(cppname, lower, upper) \ + UPB_DECLARE_DERIVED_TYPE2(cppname, upb::Def, upb::RefCounted, \ + upb_ ## lower, upb_def, upb_refcounted) \ + UPB_DEF_CASTS(lower, upper, cppname) + +UPB_DECLARE_DEF_TYPE(upb::FieldDef, fielddef, FIELD) +UPB_DECLARE_DEF_TYPE(upb::MessageDef, msgdef, MSG) +UPB_DECLARE_DEF_TYPE(upb::EnumDef, enumdef, ENUM) +UPB_DECLARE_DEF_TYPE(upb::OneofDef, oneofdef, ONEOF) + +#undef UPB_DECLARE_DEF_TYPE +#undef UPB_DEF_CASTS +#undef UPB_CPP_CASTS + + +/* upb::FieldDef **************************************************************/ + +/* The types a field can have. Note that this list is not identical to the + * types defined in descriptor.proto, which gives INT32 and SINT32 separate + * types (we distinguish the two with the "integer encoding" enum below). */ +typedef enum { + UPB_TYPE_FLOAT = 1, + UPB_TYPE_DOUBLE = 2, + UPB_TYPE_BOOL = 3, + UPB_TYPE_STRING = 4, + UPB_TYPE_BYTES = 5, + UPB_TYPE_MESSAGE = 6, + UPB_TYPE_ENUM = 7, /* Enum values are int32. */ + UPB_TYPE_INT32 = 8, + UPB_TYPE_UINT32 = 9, + UPB_TYPE_INT64 = 10, + UPB_TYPE_UINT64 = 11 +} upb_fieldtype_t; + +/* The repeated-ness of each field; this matches descriptor.proto. */ +typedef enum { + UPB_LABEL_OPTIONAL = 1, + UPB_LABEL_REQUIRED = 2, + UPB_LABEL_REPEATED = 3 +} upb_label_t; + +/* How integers should be encoded in serializations that offer multiple + * integer encoding methods. */ +typedef enum { + UPB_INTFMT_VARIABLE = 1, + UPB_INTFMT_FIXED = 2, + UPB_INTFMT_ZIGZAG = 3 /* Only for signed types (INT32/INT64). */ +} upb_intfmt_t; + +/* Descriptor types, as defined in descriptor.proto. */ +typedef enum { + UPB_DESCRIPTOR_TYPE_DOUBLE = 1, + UPB_DESCRIPTOR_TYPE_FLOAT = 2, + UPB_DESCRIPTOR_TYPE_INT64 = 3, + UPB_DESCRIPTOR_TYPE_UINT64 = 4, + UPB_DESCRIPTOR_TYPE_INT32 = 5, + UPB_DESCRIPTOR_TYPE_FIXED64 = 6, + UPB_DESCRIPTOR_TYPE_FIXED32 = 7, + UPB_DESCRIPTOR_TYPE_BOOL = 8, + UPB_DESCRIPTOR_TYPE_STRING = 9, + UPB_DESCRIPTOR_TYPE_GROUP = 10, + UPB_DESCRIPTOR_TYPE_MESSAGE = 11, + UPB_DESCRIPTOR_TYPE_BYTES = 12, + UPB_DESCRIPTOR_TYPE_UINT32 = 13, + UPB_DESCRIPTOR_TYPE_ENUM = 14, + UPB_DESCRIPTOR_TYPE_SFIXED32 = 15, + UPB_DESCRIPTOR_TYPE_SFIXED64 = 16, + UPB_DESCRIPTOR_TYPE_SINT32 = 17, + UPB_DESCRIPTOR_TYPE_SINT64 = 18 +} upb_descriptortype_t; + +/* Maximum field number allowed for FieldDefs. This is an inherent limit of the + * protobuf wire format. */ +#define UPB_MAX_FIELDNUMBER ((1 << 29) - 1) + +#ifdef __cplusplus + +/* A upb_fielddef describes a single field in a message. It is most often + * found as a part of a upb_msgdef, but can also stand alone to represent + * an extension. + * + * Its base class is upb::Def (use upb::upcast() to convert). */ +class upb::FieldDef { + public: + typedef upb_fieldtype_t Type; + typedef upb_label_t Label; + typedef upb_intfmt_t IntegerFormat; + typedef upb_descriptortype_t DescriptorType; + + /* These return true if the given value is a valid member of the enumeration. */ + static bool CheckType(int32_t val); + static bool CheckLabel(int32_t val); + static bool CheckDescriptorType(int32_t val); + static bool CheckIntegerFormat(int32_t val); + + /* These convert to the given enumeration; they require that the value is + * valid. */ + static Type ConvertType(int32_t val); + static Label ConvertLabel(int32_t val); + static DescriptorType ConvertDescriptorType(int32_t val); + static IntegerFormat ConvertIntegerFormat(int32_t val); + + /* Returns NULL if memory allocation failed. */ + static reffed_ptr New(); + + /* Duplicates the given field, returning NULL if memory allocation failed. + * When a fielddef is duplicated, the subdef (if any) is made symbolic if it + * wasn't already. If the subdef is set but has no name (which is possible + * since msgdefs are not required to have a name) the new fielddef's subdef + * will be unset. */ + FieldDef* Dup(const void* owner) const; + + /* upb::RefCounted methods like Ref()/Unref(). */ + UPB_REFCOUNTED_CPPMETHODS + + /* Functionality from upb::Def. */ + const char* full_name() const; + + bool type_is_set() const; /* set_[descriptor_]type() has been called? */ + Type type() const; /* Requires that type_is_set() == true. */ + Label label() const; /* Defaults to UPB_LABEL_OPTIONAL. */ + const char* name() const; /* NULL if uninitialized. */ + uint32_t number() const; /* Returns 0 if uninitialized. */ + bool is_extension() const; + + /* For UPB_TYPE_MESSAGE fields only where is_tag_delimited() == false, + * indicates whether this field should have lazy parsing handlers that yield + * the unparsed string for the submessage. + * + * TODO(haberman): I think we want to move this into a FieldOptions container + * when we add support for custom options (the FieldOptions struct will + * contain both regular FieldOptions like "lazy" *and* custom options). */ + bool lazy() const; + + /* For non-string, non-submessage fields, this indicates whether binary + * protobufs are encoded in packed or non-packed format. + * + * TODO(haberman): see note above about putting options like this into a + * FieldOptions container. */ + bool packed() const; + + /* An integer that can be used as an index into an array of fields for + * whatever message this field belongs to. Guaranteed to be less than + * f->containing_type()->field_count(). May only be accessed once the def has + * been finalized. */ + int index() const; + + /* The MessageDef to which this field belongs. + * + * If this field has been added to a MessageDef, that message can be retrieved + * directly (this is always the case for frozen FieldDefs). + * + * If the field has not yet been added to a MessageDef, you can set the name + * of the containing type symbolically instead. This is mostly useful for + * extensions, where the extension is declared separately from the message. */ + const MessageDef* containing_type() const; + const char* containing_type_name(); + + /* The OneofDef to which this field belongs, or NULL if this field is not part + * of a oneof. */ + const OneofDef* containing_oneof() const; + + /* The field's type according to the enum in descriptor.proto. This is not + * the same as UPB_TYPE_*, because it distinguishes between (for example) + * INT32 and SINT32, whereas our "type" enum does not. This return of + * descriptor_type() is a function of type(), integer_format(), and + * is_tag_delimited(). Likewise set_descriptor_type() sets all three + * appropriately. */ + DescriptorType descriptor_type() const; + + /* Convenient field type tests. */ + bool IsSubMessage() const; + bool IsString() const; + bool IsSequence() const; + bool IsPrimitive() const; + bool IsMap() const; + + /* How integers are encoded. Only meaningful for integer types. + * Defaults to UPB_INTFMT_VARIABLE, and is reset when "type" changes. */ + IntegerFormat integer_format() const; + + /* Whether a submessage field is tag-delimited or not (if false, then + * length-delimited). May only be set when type() == UPB_TYPE_MESSAGE. */ + bool is_tag_delimited() const; + + /* Returns the non-string default value for this fielddef, which may either + * be something the client set explicitly or the "default default" (0 for + * numbers, empty for strings). The field's type indicates the type of the + * returned value, except for enum fields that are still mutable. + * + * Requires that the given function matches the field's current type. */ + int64_t default_int64() const; + int32_t default_int32() const; + uint64_t default_uint64() const; + uint32_t default_uint32() const; + bool default_bool() const; + float default_float() const; + double default_double() const; + + /* The resulting string is always NULL-terminated. If non-NULL, the length + * will be stored in *len. */ + const char *default_string(size_t* len) const; + + /* For frozen UPB_TYPE_ENUM fields, enum defaults can always be read as either + * string or int32, and both of these methods will always return true. + * + * For mutable UPB_TYPE_ENUM fields, the story is a bit more complicated. + * Enum defaults are unusual. They can be specified either as string or int32, + * but to be valid the enum must have that value as a member. And if no + * default is specified, the "default default" comes from the EnumDef. + * + * We allow reading the default as either an int32 or a string, but only if + * we have a meaningful value to report. We have a meaningful value if it was + * set explicitly, or if we could get the "default default" from the EnumDef. + * Also if you explicitly set the name and we find the number in the EnumDef */ + bool EnumHasStringDefault() const; + bool EnumHasInt32Default() const; + + /* Submessage and enum fields must reference a "subdef", which is the + * upb::MessageDef or upb::EnumDef that defines their type. Note that when + * the FieldDef is mutable it may not have a subdef *yet*, but this function + * still returns true to indicate that the field's type requires a subdef. */ + bool HasSubDef() const; + + /* Returns the enum or submessage def for this field, if any. The field's + * type must match (ie. you may only call enum_subdef() for fields where + * type() == UPB_TYPE_ENUM). Returns NULL if the subdef has not been set or + * is currently set symbolically. */ + const EnumDef* enum_subdef() const; + const MessageDef* message_subdef() const; + + /* Returns the generic subdef for this field. Requires that HasSubDef() (ie. + * only works for UPB_TYPE_ENUM and UPB_TYPE_MESSAGE fields). */ + const Def* subdef() const; + + /* Returns the symbolic name of the subdef. If the subdef is currently set + * unresolved (ie. set symbolically) returns the symbolic name. If it has + * been resolved to a specific subdef, returns the name from that subdef. */ + const char* subdef_name() const; + + /* Setters (non-const methods), only valid for mutable FieldDefs! ***********/ + + bool set_full_name(const char* fullname, upb::Status* s); + bool set_full_name(const std::string& fullname, upb::Status* s); + + /* This may only be called if containing_type() == NULL (ie. the field has not + * been added to a message yet). */ + bool set_containing_type_name(const char *name, Status* status); + bool set_containing_type_name(const std::string& name, Status* status); + + /* Defaults to false. When we freeze, we ensure that this can only be true + * for length-delimited message fields. Prior to freezing this can be true or + * false with no restrictions. */ + void set_lazy(bool lazy); + + /* Defaults to true. Sets whether this field is encoded in packed format. */ + void set_packed(bool packed); + + /* "type" or "descriptor_type" MUST be set explicitly before the fielddef is + * finalized. These setters require that the enum value is valid; if the + * value did not come directly from an enum constant, the caller should + * validate it first with the functions above (CheckFieldType(), etc). */ + void set_type(Type type); + void set_label(Label label); + void set_descriptor_type(DescriptorType type); + void set_is_extension(bool is_extension); + + /* "number" and "name" must be set before the FieldDef is added to a + * MessageDef, and may not be set after that. + * + * "name" is the same as full_name()/set_full_name(), but since fielddefs + * most often use simple, non-qualified names, we provide this accessor + * also. Generally only extensions will want to think of this name as + * fully-qualified. */ + bool set_number(uint32_t number, upb::Status* s); + bool set_name(const char* name, upb::Status* s); + bool set_name(const std::string& name, upb::Status* s); + + void set_integer_format(IntegerFormat format); + bool set_tag_delimited(bool tag_delimited, upb::Status* s); + + /* Sets default value for the field. The call must exactly match the type + * of the field. Enum fields may use either setint32 or setstring to set + * the default numerically or symbolically, respectively, but symbolic + * defaults must be resolved before finalizing (see ResolveEnumDefault()). + * + * Changing the type of a field will reset its default. */ + void set_default_int64(int64_t val); + void set_default_int32(int32_t val); + void set_default_uint64(uint64_t val); + void set_default_uint32(uint32_t val); + void set_default_bool(bool val); + void set_default_float(float val); + void set_default_double(double val); + bool set_default_string(const void *str, size_t len, Status *s); + bool set_default_string(const std::string &str, Status *s); + void set_default_cstr(const char *str, Status *s); + + /* Before a fielddef is frozen, its subdef may be set either directly (with a + * upb::Def*) or symbolically. Symbolic refs must be resolved before the + * containing msgdef can be frozen (see upb_resolve() above). upb always + * guarantees that any def reachable from a live def will also be kept alive. + * + * Both methods require that upb_hassubdef(f) (so the type must be set prior + * to calling these methods). Returns false if this is not the case, or if + * the given subdef is not of the correct type. The subdef is reset if the + * field's type is changed. The subdef can be set to NULL to clear it. */ + bool set_subdef(const Def* subdef, Status* s); + bool set_enum_subdef(const EnumDef* subdef, Status* s); + bool set_message_subdef(const MessageDef* subdef, Status* s); + bool set_subdef_name(const char* name, Status* s); + bool set_subdef_name(const std::string &name, Status* s); + + private: + UPB_DISALLOW_POD_OPS(FieldDef, upb::FieldDef) +}; + +# endif /* defined(__cplusplus) */ + +UPB_BEGIN_EXTERN_C + +/* Native C API. */ +upb_fielddef *upb_fielddef_new(const void *owner); +upb_fielddef *upb_fielddef_dup(const upb_fielddef *f, const void *owner); + +/* Include upb_refcounted methods like upb_fielddef_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_fielddef, upb_fielddef_upcast2) + +/* Methods from upb_def. */ +const char *upb_fielddef_fullname(const upb_fielddef *f); +bool upb_fielddef_setfullname(upb_fielddef *f, const char *fullname, + upb_status *s); + +bool upb_fielddef_typeisset(const upb_fielddef *f); +upb_fieldtype_t upb_fielddef_type(const upb_fielddef *f); +upb_descriptortype_t upb_fielddef_descriptortype(const upb_fielddef *f); +upb_label_t upb_fielddef_label(const upb_fielddef *f); +uint32_t upb_fielddef_number(const upb_fielddef *f); +const char *upb_fielddef_name(const upb_fielddef *f); +bool upb_fielddef_isextension(const upb_fielddef *f); +bool upb_fielddef_lazy(const upb_fielddef *f); +bool upb_fielddef_packed(const upb_fielddef *f); +const upb_msgdef *upb_fielddef_containingtype(const upb_fielddef *f); +const upb_oneofdef *upb_fielddef_containingoneof(const upb_fielddef *f); +upb_msgdef *upb_fielddef_containingtype_mutable(upb_fielddef *f); +const char *upb_fielddef_containingtypename(upb_fielddef *f); +upb_intfmt_t upb_fielddef_intfmt(const upb_fielddef *f); +uint32_t upb_fielddef_index(const upb_fielddef *f); +bool upb_fielddef_istagdelim(const upb_fielddef *f); +bool upb_fielddef_issubmsg(const upb_fielddef *f); +bool upb_fielddef_isstring(const upb_fielddef *f); +bool upb_fielddef_isseq(const upb_fielddef *f); +bool upb_fielddef_isprimitive(const upb_fielddef *f); +bool upb_fielddef_ismap(const upb_fielddef *f); +int64_t upb_fielddef_defaultint64(const upb_fielddef *f); +int32_t upb_fielddef_defaultint32(const upb_fielddef *f); +uint64_t upb_fielddef_defaultuint64(const upb_fielddef *f); +uint32_t upb_fielddef_defaultuint32(const upb_fielddef *f); +bool upb_fielddef_defaultbool(const upb_fielddef *f); +float upb_fielddef_defaultfloat(const upb_fielddef *f); +double upb_fielddef_defaultdouble(const upb_fielddef *f); +const char *upb_fielddef_defaultstr(const upb_fielddef *f, size_t *len); +bool upb_fielddef_enumhasdefaultint32(const upb_fielddef *f); +bool upb_fielddef_enumhasdefaultstr(const upb_fielddef *f); +bool upb_fielddef_hassubdef(const upb_fielddef *f); +const upb_def *upb_fielddef_subdef(const upb_fielddef *f); +const upb_msgdef *upb_fielddef_msgsubdef(const upb_fielddef *f); +const upb_enumdef *upb_fielddef_enumsubdef(const upb_fielddef *f); +const char *upb_fielddef_subdefname(const upb_fielddef *f); + +void upb_fielddef_settype(upb_fielddef *f, upb_fieldtype_t type); +void upb_fielddef_setdescriptortype(upb_fielddef *f, int type); +void upb_fielddef_setlabel(upb_fielddef *f, upb_label_t label); +bool upb_fielddef_setnumber(upb_fielddef *f, uint32_t number, upb_status *s); +bool upb_fielddef_setname(upb_fielddef *f, const char *name, upb_status *s); +bool upb_fielddef_setcontainingtypename(upb_fielddef *f, const char *name, + upb_status *s); +void upb_fielddef_setisextension(upb_fielddef *f, bool is_extension); +void upb_fielddef_setlazy(upb_fielddef *f, bool lazy); +void upb_fielddef_setpacked(upb_fielddef *f, bool packed); +void upb_fielddef_setintfmt(upb_fielddef *f, upb_intfmt_t fmt); +void upb_fielddef_settagdelim(upb_fielddef *f, bool tag_delim); +void upb_fielddef_setdefaultint64(upb_fielddef *f, int64_t val); +void upb_fielddef_setdefaultint32(upb_fielddef *f, int32_t val); +void upb_fielddef_setdefaultuint64(upb_fielddef *f, uint64_t val); +void upb_fielddef_setdefaultuint32(upb_fielddef *f, uint32_t val); +void upb_fielddef_setdefaultbool(upb_fielddef *f, bool val); +void upb_fielddef_setdefaultfloat(upb_fielddef *f, float val); +void upb_fielddef_setdefaultdouble(upb_fielddef *f, double val); +bool upb_fielddef_setdefaultstr(upb_fielddef *f, const void *str, size_t len, + upb_status *s); +void upb_fielddef_setdefaultcstr(upb_fielddef *f, const char *str, + upb_status *s); +bool upb_fielddef_setsubdef(upb_fielddef *f, const upb_def *subdef, + upb_status *s); +bool upb_fielddef_setmsgsubdef(upb_fielddef *f, const upb_msgdef *subdef, + upb_status *s); +bool upb_fielddef_setenumsubdef(upb_fielddef *f, const upb_enumdef *subdef, + upb_status *s); +bool upb_fielddef_setsubdefname(upb_fielddef *f, const char *name, + upb_status *s); + +bool upb_fielddef_checklabel(int32_t label); +bool upb_fielddef_checktype(int32_t type); +bool upb_fielddef_checkdescriptortype(int32_t type); +bool upb_fielddef_checkintfmt(int32_t fmt); + +UPB_END_EXTERN_C + + +/* upb::MessageDef ************************************************************/ + +typedef upb_inttable_iter upb_msg_field_iter; +typedef upb_strtable_iter upb_msg_oneof_iter; + +#ifdef __cplusplus + +/* Structure that describes a single .proto message type. + * + * Its base class is upb::Def (use upb::upcast() to convert). */ +class upb::MessageDef { + public: + /* Returns NULL if memory allocation failed. */ + static reffed_ptr New(); + + /* upb::RefCounted methods like Ref()/Unref(). */ + UPB_REFCOUNTED_CPPMETHODS + + /* Functionality from upb::Def. */ + const char* full_name() const; + bool set_full_name(const char* fullname, Status* s); + bool set_full_name(const std::string& fullname, Status* s); + + /* Call to freeze this MessageDef. + * WARNING: this will fail if this message has any unfrozen submessages! + * Messages with cycles must be frozen as a batch using upb::Def::Freeze(). */ + bool Freeze(Status* s); + + /* The number of fields that belong to the MessageDef. */ + int field_count() const; + + /* The number of oneofs that belong to the MessageDef. */ + int oneof_count() const; + + /* Adds a field (upb_fielddef object) to a msgdef. Requires that the msgdef + * and the fielddefs are mutable. The fielddef's name and number must be + * set, and the message may not already contain any field with this name or + * number, and this fielddef may not be part of another message. In error + * cases false is returned and the msgdef is unchanged. + * + * If the given field is part of a oneof, this call succeeds if and only if + * that oneof is already part of this msgdef. (Note that adding a oneof to a + * msgdef automatically adds all of its fields to the msgdef at the time that + * the oneof is added, so it is usually more idiomatic to add the oneof's + * fields first then add the oneof to the msgdef. This case is supported for + * convenience.) + * + * If |f| is already part of this MessageDef, this method performs no action + * and returns true (success). Thus, this method is idempotent. */ + bool AddField(FieldDef* f, Status* s); + bool AddField(const reffed_ptr& f, Status* s); + + /* Adds a oneof (upb_oneofdef object) to a msgdef. Requires that the msgdef, + * oneof, and any fielddefs are mutable, that the fielddefs contained in the + * oneof do not have any name or number conflicts with existing fields in the + * msgdef, and that the oneof's name is unique among all oneofs in the msgdef. + * If the oneof is added successfully, all of its fields will be added + * directly to the msgdef as well. In error cases, false is returned and the + * msgdef is unchanged. */ + bool AddOneof(OneofDef* o, Status* s); + bool AddOneof(const reffed_ptr& o, Status* s); + + /* These return NULL if the field is not found. */ + FieldDef* FindFieldByNumber(uint32_t number); + FieldDef* FindFieldByName(const char *name, size_t len); + const FieldDef* FindFieldByNumber(uint32_t number) const; + const FieldDef* FindFieldByName(const char* name, size_t len) const; + + + FieldDef* FindFieldByName(const char *name) { + return FindFieldByName(name, strlen(name)); + } + const FieldDef* FindFieldByName(const char *name) const { + return FindFieldByName(name, strlen(name)); + } + + template + FieldDef* FindFieldByName(const T& str) { + return FindFieldByName(str.c_str(), str.size()); + } + template + const FieldDef* FindFieldByName(const T& str) const { + return FindFieldByName(str.c_str(), str.size()); + } + + OneofDef* FindOneofByName(const char* name, size_t len); + const OneofDef* FindOneofByName(const char* name, size_t len) const; + + OneofDef* FindOneofByName(const char* name) { + return FindOneofByName(name, strlen(name)); + } + const OneofDef* FindOneofByName(const char* name) const { + return FindOneofByName(name, strlen(name)); + } + + template + OneofDef* FindOneofByName(const T& str) { + return FindOneofByName(str.c_str(), str.size()); + } + template + const OneofDef* FindOneofByName(const T& str) const { + return FindOneofByName(str.c_str(), str.size()); + } + + /* Returns a new msgdef that is a copy of the given msgdef (and a copy of all + * the fields) but with any references to submessages broken and replaced + * with just the name of the submessage. Returns NULL if memory allocation + * failed. + * + * TODO(haberman): which is more useful, keeping fields resolved or + * unresolving them? If there's no obvious answer, Should this functionality + * just be moved into symtab.c? */ + MessageDef* Dup(const void* owner) const; + + /* Is this message a map entry? */ + void setmapentry(bool map_entry); + bool mapentry() const; + + /* Iteration over fields. The order is undefined. */ + class field_iterator + : public std::iterator { + public: + explicit field_iterator(MessageDef* md); + static field_iterator end(MessageDef* md); + + void operator++(); + FieldDef* operator*() const; + bool operator!=(const field_iterator& other) const; + bool operator==(const field_iterator& other) const; + + private: + upb_msg_field_iter iter_; + }; + + class const_field_iterator + : public std::iterator { + public: + explicit const_field_iterator(const MessageDef* md); + static const_field_iterator end(const MessageDef* md); + + void operator++(); + const FieldDef* operator*() const; + bool operator!=(const const_field_iterator& other) const; + bool operator==(const const_field_iterator& other) const; + + private: + upb_msg_field_iter iter_; + }; + + /* Iteration over oneofs. The order is undefined. */ + class oneof_iterator + : public std::iterator { + public: + explicit oneof_iterator(MessageDef* md); + static oneof_iterator end(MessageDef* md); + + void operator++(); + OneofDef* operator*() const; + bool operator!=(const oneof_iterator& other) const; + bool operator==(const oneof_iterator& other) const; + + private: + upb_msg_oneof_iter iter_; + }; + + class const_oneof_iterator + : public std::iterator { + public: + explicit const_oneof_iterator(const MessageDef* md); + static const_oneof_iterator end(const MessageDef* md); + + void operator++(); + const OneofDef* operator*() const; + bool operator!=(const const_oneof_iterator& other) const; + bool operator==(const const_oneof_iterator& other) const; + + private: + upb_msg_oneof_iter iter_; + }; + + class FieldAccessor { + public: + explicit FieldAccessor(MessageDef* msg) : msg_(msg) {} + field_iterator begin() { return msg_->field_begin(); } + field_iterator end() { return msg_->field_end(); } + private: + MessageDef* msg_; + }; + + class ConstFieldAccessor { + public: + explicit ConstFieldAccessor(const MessageDef* msg) : msg_(msg) {} + const_field_iterator begin() { return msg_->field_begin(); } + const_field_iterator end() { return msg_->field_end(); } + private: + const MessageDef* msg_; + }; + + class OneofAccessor { + public: + explicit OneofAccessor(MessageDef* msg) : msg_(msg) {} + oneof_iterator begin() { return msg_->oneof_begin(); } + oneof_iterator end() { return msg_->oneof_end(); } + private: + MessageDef* msg_; + }; + + class ConstOneofAccessor { + public: + explicit ConstOneofAccessor(const MessageDef* msg) : msg_(msg) {} + const_oneof_iterator begin() { return msg_->oneof_begin(); } + const_oneof_iterator end() { return msg_->oneof_end(); } + private: + const MessageDef* msg_; + }; + + field_iterator field_begin(); + field_iterator field_end(); + const_field_iterator field_begin() const; + const_field_iterator field_end() const; + + oneof_iterator oneof_begin(); + oneof_iterator oneof_end(); + const_oneof_iterator oneof_begin() const; + const_oneof_iterator oneof_end() const; + + FieldAccessor fields() { return FieldAccessor(this); } + ConstFieldAccessor fields() const { return ConstFieldAccessor(this); } + OneofAccessor oneofs() { return OneofAccessor(this); } + ConstOneofAccessor oneofs() const { return ConstOneofAccessor(this); } + + private: + UPB_DISALLOW_POD_OPS(MessageDef, upb::MessageDef) +}; + +#endif /* __cplusplus */ + +UPB_BEGIN_EXTERN_C + +/* Returns NULL if memory allocation failed. */ +upb_msgdef *upb_msgdef_new(const void *owner); + +/* Include upb_refcounted methods like upb_msgdef_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_msgdef, upb_msgdef_upcast2) + +bool upb_msgdef_freeze(upb_msgdef *m, upb_status *status); + +const char *upb_msgdef_fullname(const upb_msgdef *m); +bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, upb_status *s); + +upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner); +bool upb_msgdef_addfield(upb_msgdef *m, upb_fielddef *f, const void *ref_donor, + upb_status *s); +bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor, + upb_status *s); + +/* Field lookup in a couple of different variations: + * - itof = int to field + * - ntof = name to field + * - ntofz = name to field, null-terminated string. */ +const upb_fielddef *upb_msgdef_itof(const upb_msgdef *m, uint32_t i); +const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name, + size_t len); +int upb_msgdef_numfields(const upb_msgdef *m); + +UPB_INLINE const upb_fielddef *upb_msgdef_ntofz(const upb_msgdef *m, + const char *name) { + return upb_msgdef_ntof(m, name, strlen(name)); +} + +UPB_INLINE upb_fielddef *upb_msgdef_itof_mutable(upb_msgdef *m, uint32_t i) { + return (upb_fielddef*)upb_msgdef_itof(m, i); +} + +UPB_INLINE upb_fielddef *upb_msgdef_ntof_mutable(upb_msgdef *m, + const char *name, size_t len) { + return (upb_fielddef *)upb_msgdef_ntof(m, name, len); +} + +/* Oneof lookup: + * - ntoo = name to oneof + * - ntooz = name to oneof, null-terminated string. */ +const upb_oneofdef *upb_msgdef_ntoo(const upb_msgdef *m, const char *name, + size_t len); +int upb_msgdef_numoneofs(const upb_msgdef *m); + +UPB_INLINE const upb_oneofdef *upb_msgdef_ntooz(const upb_msgdef *m, + const char *name) { + return upb_msgdef_ntoo(m, name, strlen(name)); +} + +UPB_INLINE upb_oneofdef *upb_msgdef_ntoo_mutable(upb_msgdef *m, + const char *name, size_t len) { + return (upb_oneofdef *)upb_msgdef_ntoo(m, name, len); +} + +void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry); +bool upb_msgdef_mapentry(const upb_msgdef *m); + +/* Well-known field tag numbers for map-entry messages. */ +#define UPB_MAPENTRY_KEY 1 +#define UPB_MAPENTRY_VALUE 2 + +const upb_oneofdef *upb_msgdef_findoneof(const upb_msgdef *m, + const char *name); +int upb_msgdef_numoneofs(const upb_msgdef *m); + +/* upb_msg_field_iter i; + * for(upb_msg_field_begin(&i, m); + * !upb_msg_field_done(&i); + * upb_msg_field_next(&i)) { + * upb_fielddef *f = upb_msg_iter_field(&i); + * // ... + * } + * + * For C we don't have separate iterators for const and non-const. + * It is the caller's responsibility to cast the upb_fielddef* to + * const if the upb_msgdef* is const. */ +void upb_msg_field_begin(upb_msg_field_iter *iter, const upb_msgdef *m); +void upb_msg_field_next(upb_msg_field_iter *iter); +bool upb_msg_field_done(const upb_msg_field_iter *iter); +upb_fielddef *upb_msg_iter_field(const upb_msg_field_iter *iter); +void upb_msg_field_iter_setdone(upb_msg_field_iter *iter); + +/* Similar to above, we also support iterating through the oneofs in a + * msgdef. */ +void upb_msg_oneof_begin(upb_msg_oneof_iter *iter, const upb_msgdef *m); +void upb_msg_oneof_next(upb_msg_oneof_iter *iter); +bool upb_msg_oneof_done(const upb_msg_oneof_iter *iter); +upb_oneofdef *upb_msg_iter_oneof(const upb_msg_oneof_iter *iter); +void upb_msg_oneof_iter_setdone(upb_msg_oneof_iter *iter); + +UPB_END_EXTERN_C + + +/* upb::EnumDef ***************************************************************/ + +typedef upb_strtable_iter upb_enum_iter; + +#ifdef __cplusplus + +/* Class that represents an enum. Its base class is upb::Def (convert with + * upb::upcast()). */ +class upb::EnumDef { + public: + /* Returns NULL if memory allocation failed. */ + static reffed_ptr New(); + + /* upb::RefCounted methods like Ref()/Unref(). */ + UPB_REFCOUNTED_CPPMETHODS + + /* Functionality from upb::Def. */ + const char* full_name() const; + bool set_full_name(const char* fullname, Status* s); + bool set_full_name(const std::string& fullname, Status* s); + + /* Call to freeze this EnumDef. */ + bool Freeze(Status* s); + + /* The value that is used as the default when no field default is specified. + * If not set explicitly, the first value that was added will be used. + * The default value must be a member of the enum. + * Requires that value_count() > 0. */ + int32_t default_value() const; + + /* Sets the default value. If this value is not valid, returns false and an + * error message in status. */ + bool set_default_value(int32_t val, Status* status); + + /* Returns the number of values currently defined in the enum. Note that + * multiple names can refer to the same number, so this may be greater than + * the total number of unique numbers. */ + int value_count() const; + + /* Adds a single name/number pair to the enum. Fails if this name has + * already been used by another value. */ + bool AddValue(const char* name, int32_t num, Status* status); + bool AddValue(const std::string& name, int32_t num, Status* status); + + /* Lookups from name to integer, returning true if found. */ + bool FindValueByName(const char* name, int32_t* num) const; + + /* Finds the name corresponding to the given number, or NULL if none was + * found. If more than one name corresponds to this number, returns the + * first one that was added. */ + const char* FindValueByNumber(int32_t num) const; + + /* Returns a new EnumDef with all the same values. The new EnumDef will be + * owned by the given owner. */ + EnumDef* Dup(const void* owner) const; + + /* Iteration over name/value pairs. The order is undefined. + * Adding an enum val invalidates any iterators. + * + * TODO: make compatible with range-for, with elements as pairs? */ + class Iterator { + public: + explicit Iterator(const EnumDef*); + + int32_t number(); + const char *name(); + bool Done(); + void Next(); + + private: + upb_enum_iter iter_; + }; + + private: + UPB_DISALLOW_POD_OPS(EnumDef, upb::EnumDef) +}; + +#endif /* __cplusplus */ + +UPB_BEGIN_EXTERN_C + +/* Native C API. */ +upb_enumdef *upb_enumdef_new(const void *owner); +upb_enumdef *upb_enumdef_dup(const upb_enumdef *e, const void *owner); + +/* Include upb_refcounted methods like upb_enumdef_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_enumdef, upb_enumdef_upcast2) + +bool upb_enumdef_freeze(upb_enumdef *e, upb_status *status); + +/* From upb_def. */ +const char *upb_enumdef_fullname(const upb_enumdef *e); +bool upb_enumdef_setfullname(upb_enumdef *e, const char *fullname, + upb_status *s); + +int32_t upb_enumdef_default(const upb_enumdef *e); +bool upb_enumdef_setdefault(upb_enumdef *e, int32_t val, upb_status *s); +int upb_enumdef_numvals(const upb_enumdef *e); +bool upb_enumdef_addval(upb_enumdef *e, const char *name, int32_t num, + upb_status *status); + +/* Enum lookups: + * - ntoi: look up a name with specified length. + * - ntoiz: look up a name provided as a null-terminated string. + * - iton: look up an integer, returning the name as a null-terminated + * string. */ +bool upb_enumdef_ntoi(const upb_enumdef *e, const char *name, size_t len, + int32_t *num); +UPB_INLINE bool upb_enumdef_ntoiz(const upb_enumdef *e, + const char *name, int32_t *num) { + return upb_enumdef_ntoi(e, name, strlen(name), num); +} +const char *upb_enumdef_iton(const upb_enumdef *e, int32_t num); + +/* upb_enum_iter i; + * for(upb_enum_begin(&i, e); !upb_enum_done(&i); upb_enum_next(&i)) { + * // ... + * } + */ +void upb_enum_begin(upb_enum_iter *iter, const upb_enumdef *e); +void upb_enum_next(upb_enum_iter *iter); +bool upb_enum_done(upb_enum_iter *iter); +const char *upb_enum_iter_name(upb_enum_iter *iter); +int32_t upb_enum_iter_number(upb_enum_iter *iter); + +UPB_END_EXTERN_C + +/* upb::OneofDef **************************************************************/ + +typedef upb_inttable_iter upb_oneof_iter; + +#ifdef __cplusplus + +/* Class that represents a oneof. Its base class is upb::Def (convert with + * upb::upcast()). */ +class upb::OneofDef { + public: + /* Returns NULL if memory allocation failed. */ + static reffed_ptr New(); + + /* upb::RefCounted methods like Ref()/Unref(). */ + UPB_REFCOUNTED_CPPMETHODS + + /* Functionality from upb::Def. */ + const char* full_name() const; + + /* Returns the MessageDef that owns this OneofDef. */ + const MessageDef* containing_type() const; + + /* Returns the name of this oneof. This is the name used to look up the oneof + * by name once added to a message def. */ + const char* name() const; + bool set_name(const char* name, Status* s); + + /* Returns the number of fields currently defined in the oneof. */ + int field_count() const; + + /* Adds a field to the oneof. The field must not have been added to any other + * oneof or msgdef. If the oneof is not yet part of a msgdef, then when the + * oneof is eventually added to a msgdef, all fields added to the oneof will + * also be added to the msgdef at that time. If the oneof is already part of a + * msgdef, the field must either be a part of that msgdef already, or must not + * be a part of any msgdef; in the latter case, the field is added to the + * msgdef as a part of this operation. + * + * The field may only have an OPTIONAL label, never REQUIRED or REPEATED. + * + * If |f| is already part of this MessageDef, this method performs no action + * and returns true (success). Thus, this method is idempotent. */ + bool AddField(FieldDef* field, Status* s); + bool AddField(const reffed_ptr& field, Status* s); + + /* Looks up by name. */ + const FieldDef* FindFieldByName(const char* name, size_t len) const; + FieldDef* FindFieldByName(const char* name, size_t len); + const FieldDef* FindFieldByName(const char* name) const { + return FindFieldByName(name, strlen(name)); + } + FieldDef* FindFieldByName(const char* name) { + return FindFieldByName(name, strlen(name)); + } + + template + FieldDef* FindFieldByName(const T& str) { + return FindFieldByName(str.c_str(), str.size()); + } + template + const FieldDef* FindFieldByName(const T& str) const { + return FindFieldByName(str.c_str(), str.size()); + } + + /* Looks up by tag number. */ + const FieldDef* FindFieldByNumber(uint32_t num) const; + + /* Returns a new OneofDef with all the same fields. The OneofDef will be owned + * by the given owner. */ + OneofDef* Dup(const void* owner) const; + + /* Iteration over fields. The order is undefined. */ + class iterator : public std::iterator { + public: + explicit iterator(OneofDef* md); + static iterator end(OneofDef* md); + + void operator++(); + FieldDef* operator*() const; + bool operator!=(const iterator& other) const; + bool operator==(const iterator& other) const; + + private: + upb_oneof_iter iter_; + }; + + class const_iterator + : public std::iterator { + public: + explicit const_iterator(const OneofDef* md); + static const_iterator end(const OneofDef* md); + + void operator++(); + const FieldDef* operator*() const; + bool operator!=(const const_iterator& other) const; + bool operator==(const const_iterator& other) const; + + private: + upb_oneof_iter iter_; + }; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + private: + UPB_DISALLOW_POD_OPS(OneofDef, upb::OneofDef) +}; + +#endif /* __cplusplus */ + +UPB_BEGIN_EXTERN_C + +/* Native C API. */ +upb_oneofdef *upb_oneofdef_new(const void *owner); +upb_oneofdef *upb_oneofdef_dup(const upb_oneofdef *o, const void *owner); + +/* Include upb_refcounted methods like upb_oneofdef_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_oneofdef, upb_oneofdef_upcast2) + +const char *upb_oneofdef_name(const upb_oneofdef *o); +bool upb_oneofdef_setname(upb_oneofdef *o, const char *name, upb_status *s); + +const upb_msgdef *upb_oneofdef_containingtype(const upb_oneofdef *o); +int upb_oneofdef_numfields(const upb_oneofdef *o); +bool upb_oneofdef_addfield(upb_oneofdef *o, upb_fielddef *f, + const void *ref_donor, + upb_status *s); + +/* Oneof lookups: + * - ntof: look up a field by name. + * - ntofz: look up a field by name (as a null-terminated string). + * - itof: look up a field by number. */ +const upb_fielddef *upb_oneofdef_ntof(const upb_oneofdef *o, + const char *name, size_t length); +UPB_INLINE const upb_fielddef *upb_oneofdef_ntofz(const upb_oneofdef *o, + const char *name) { + return upb_oneofdef_ntof(o, name, strlen(name)); +} +const upb_fielddef *upb_oneofdef_itof(const upb_oneofdef *o, uint32_t num); + +/* upb_oneof_iter i; + * for(upb_oneof_begin(&i, e); !upb_oneof_done(&i); upb_oneof_next(&i)) { + * // ... + * } + */ +void upb_oneof_begin(upb_oneof_iter *iter, const upb_oneofdef *o); +void upb_oneof_next(upb_oneof_iter *iter); +bool upb_oneof_done(upb_oneof_iter *iter); +upb_fielddef *upb_oneof_iter_field(const upb_oneof_iter *iter); +void upb_oneof_iter_setdone(upb_oneof_iter *iter); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +UPB_INLINE const char* upb_safecstr(const std::string& str) { + assert(str.size() == std::strlen(str.c_str())); + return str.c_str(); +} + +/* Inline C++ wrappers. */ +namespace upb { + +inline Def* Def::Dup(const void* owner) const { + return upb_def_dup(this, owner); +} +inline Def::Type Def::def_type() const { return upb_def_type(this); } +inline const char* Def::full_name() const { return upb_def_fullname(this); } +inline bool Def::set_full_name(const char* fullname, Status* s) { + return upb_def_setfullname(this, fullname, s); +} +inline bool Def::set_full_name(const std::string& fullname, Status* s) { + return upb_def_setfullname(this, upb_safecstr(fullname), s); +} +inline bool Def::Freeze(Def* const* defs, int n, Status* status) { + return upb_def_freeze(defs, n, status); +} +inline bool Def::Freeze(const std::vector& defs, Status* status) { + return upb_def_freeze((Def* const*)&defs[0], defs.size(), status); +} + +inline bool FieldDef::CheckType(int32_t val) { + return upb_fielddef_checktype(val); +} +inline bool FieldDef::CheckLabel(int32_t val) { + return upb_fielddef_checklabel(val); +} +inline bool FieldDef::CheckDescriptorType(int32_t val) { + return upb_fielddef_checkdescriptortype(val); +} +inline bool FieldDef::CheckIntegerFormat(int32_t val) { + return upb_fielddef_checkintfmt(val); +} +inline FieldDef::Type FieldDef::ConvertType(int32_t val) { + assert(CheckType(val)); + return static_cast(val); +} +inline FieldDef::Label FieldDef::ConvertLabel(int32_t val) { + assert(CheckLabel(val)); + return static_cast(val); +} +inline FieldDef::DescriptorType FieldDef::ConvertDescriptorType(int32_t val) { + assert(CheckDescriptorType(val)); + return static_cast(val); +} +inline FieldDef::IntegerFormat FieldDef::ConvertIntegerFormat(int32_t val) { + assert(CheckIntegerFormat(val)); + return static_cast(val); +} + +inline reffed_ptr FieldDef::New() { + upb_fielddef *f = upb_fielddef_new(&f); + return reffed_ptr(f, &f); +} +inline FieldDef* FieldDef::Dup(const void* owner) const { + return upb_fielddef_dup(this, owner); +} +inline const char* FieldDef::full_name() const { + return upb_fielddef_fullname(this); +} +inline bool FieldDef::set_full_name(const char* fullname, Status* s) { + return upb_fielddef_setfullname(this, fullname, s); +} +inline bool FieldDef::set_full_name(const std::string& fullname, Status* s) { + return upb_fielddef_setfullname(this, upb_safecstr(fullname), s); +} +inline bool FieldDef::type_is_set() const { + return upb_fielddef_typeisset(this); +} +inline FieldDef::Type FieldDef::type() const { return upb_fielddef_type(this); } +inline FieldDef::DescriptorType FieldDef::descriptor_type() const { + return upb_fielddef_descriptortype(this); +} +inline FieldDef::Label FieldDef::label() const { + return upb_fielddef_label(this); +} +inline uint32_t FieldDef::number() const { return upb_fielddef_number(this); } +inline const char* FieldDef::name() const { return upb_fielddef_name(this); } +inline bool FieldDef::is_extension() const { + return upb_fielddef_isextension(this); +} +inline bool FieldDef::lazy() const { + return upb_fielddef_lazy(this); +} +inline void FieldDef::set_lazy(bool lazy) { + upb_fielddef_setlazy(this, lazy); +} +inline bool FieldDef::packed() const { + return upb_fielddef_packed(this); +} +inline void FieldDef::set_packed(bool packed) { + upb_fielddef_setpacked(this, packed); +} +inline const MessageDef* FieldDef::containing_type() const { + return upb_fielddef_containingtype(this); +} +inline const OneofDef* FieldDef::containing_oneof() const { + return upb_fielddef_containingoneof(this); +} +inline const char* FieldDef::containing_type_name() { + return upb_fielddef_containingtypename(this); +} +inline bool FieldDef::set_number(uint32_t number, Status* s) { + return upb_fielddef_setnumber(this, number, s); +} +inline bool FieldDef::set_name(const char *name, Status* s) { + return upb_fielddef_setname(this, name, s); +} +inline bool FieldDef::set_name(const std::string& name, Status* s) { + return upb_fielddef_setname(this, upb_safecstr(name), s); +} +inline bool FieldDef::set_containing_type_name(const char *name, Status* s) { + return upb_fielddef_setcontainingtypename(this, name, s); +} +inline bool FieldDef::set_containing_type_name(const std::string &name, + Status *s) { + return upb_fielddef_setcontainingtypename(this, upb_safecstr(name), s); +} +inline void FieldDef::set_type(upb_fieldtype_t type) { + upb_fielddef_settype(this, type); +} +inline void FieldDef::set_is_extension(bool is_extension) { + upb_fielddef_setisextension(this, is_extension); +} +inline void FieldDef::set_descriptor_type(FieldDef::DescriptorType type) { + upb_fielddef_setdescriptortype(this, type); +} +inline void FieldDef::set_label(upb_label_t label) { + upb_fielddef_setlabel(this, label); +} +inline bool FieldDef::IsSubMessage() const { + return upb_fielddef_issubmsg(this); +} +inline bool FieldDef::IsString() const { return upb_fielddef_isstring(this); } +inline bool FieldDef::IsSequence() const { return upb_fielddef_isseq(this); } +inline bool FieldDef::IsMap() const { return upb_fielddef_ismap(this); } +inline int64_t FieldDef::default_int64() const { + return upb_fielddef_defaultint64(this); +} +inline int32_t FieldDef::default_int32() const { + return upb_fielddef_defaultint32(this); +} +inline uint64_t FieldDef::default_uint64() const { + return upb_fielddef_defaultuint64(this); +} +inline uint32_t FieldDef::default_uint32() const { + return upb_fielddef_defaultuint32(this); +} +inline bool FieldDef::default_bool() const { + return upb_fielddef_defaultbool(this); +} +inline float FieldDef::default_float() const { + return upb_fielddef_defaultfloat(this); +} +inline double FieldDef::default_double() const { + return upb_fielddef_defaultdouble(this); +} +inline const char* FieldDef::default_string(size_t* len) const { + return upb_fielddef_defaultstr(this, len); +} +inline void FieldDef::set_default_int64(int64_t value) { + upb_fielddef_setdefaultint64(this, value); +} +inline void FieldDef::set_default_int32(int32_t value) { + upb_fielddef_setdefaultint32(this, value); +} +inline void FieldDef::set_default_uint64(uint64_t value) { + upb_fielddef_setdefaultuint64(this, value); +} +inline void FieldDef::set_default_uint32(uint32_t value) { + upb_fielddef_setdefaultuint32(this, value); +} +inline void FieldDef::set_default_bool(bool value) { + upb_fielddef_setdefaultbool(this, value); +} +inline void FieldDef::set_default_float(float value) { + upb_fielddef_setdefaultfloat(this, value); +} +inline void FieldDef::set_default_double(double value) { + upb_fielddef_setdefaultdouble(this, value); +} +inline bool FieldDef::set_default_string(const void *str, size_t len, + Status *s) { + return upb_fielddef_setdefaultstr(this, str, len, s); +} +inline bool FieldDef::set_default_string(const std::string& str, Status* s) { + return upb_fielddef_setdefaultstr(this, str.c_str(), str.size(), s); +} +inline void FieldDef::set_default_cstr(const char* str, Status* s) { + return upb_fielddef_setdefaultcstr(this, str, s); +} +inline bool FieldDef::HasSubDef() const { return upb_fielddef_hassubdef(this); } +inline const Def* FieldDef::subdef() const { return upb_fielddef_subdef(this); } +inline const MessageDef *FieldDef::message_subdef() const { + return upb_fielddef_msgsubdef(this); +} +inline const EnumDef *FieldDef::enum_subdef() const { + return upb_fielddef_enumsubdef(this); +} +inline const char* FieldDef::subdef_name() const { + return upb_fielddef_subdefname(this); +} +inline bool FieldDef::set_subdef(const Def* subdef, Status* s) { + return upb_fielddef_setsubdef(this, subdef, s); +} +inline bool FieldDef::set_enum_subdef(const EnumDef* subdef, Status* s) { + return upb_fielddef_setenumsubdef(this, subdef, s); +} +inline bool FieldDef::set_message_subdef(const MessageDef* subdef, Status* s) { + return upb_fielddef_setmsgsubdef(this, subdef, s); +} +inline bool FieldDef::set_subdef_name(const char* name, Status* s) { + return upb_fielddef_setsubdefname(this, name, s); +} +inline bool FieldDef::set_subdef_name(const std::string& name, Status* s) { + return upb_fielddef_setsubdefname(this, upb_safecstr(name), s); +} + +inline reffed_ptr MessageDef::New() { + upb_msgdef *m = upb_msgdef_new(&m); + return reffed_ptr(m, &m); +} +inline const char *MessageDef::full_name() const { + return upb_msgdef_fullname(this); +} +inline bool MessageDef::set_full_name(const char* fullname, Status* s) { + return upb_msgdef_setfullname(this, fullname, s); +} +inline bool MessageDef::set_full_name(const std::string& fullname, Status* s) { + return upb_msgdef_setfullname(this, upb_safecstr(fullname), s); +} +inline bool MessageDef::Freeze(Status* status) { + return upb_msgdef_freeze(this, status); +} +inline int MessageDef::field_count() const { + return upb_msgdef_numfields(this); +} +inline int MessageDef::oneof_count() const { + return upb_msgdef_numoneofs(this); +} +inline bool MessageDef::AddField(upb_fielddef* f, Status* s) { + return upb_msgdef_addfield(this, f, NULL, s); +} +inline bool MessageDef::AddField(const reffed_ptr& f, Status* s) { + return upb_msgdef_addfield(this, f.get(), NULL, s); +} +inline bool MessageDef::AddOneof(upb_oneofdef* o, Status* s) { + return upb_msgdef_addoneof(this, o, NULL, s); +} +inline bool MessageDef::AddOneof(const reffed_ptr& o, Status* s) { + return upb_msgdef_addoneof(this, o.get(), NULL, s); +} +inline FieldDef* MessageDef::FindFieldByNumber(uint32_t number) { + return upb_msgdef_itof_mutable(this, number); +} +inline FieldDef* MessageDef::FindFieldByName(const char* name, size_t len) { + return upb_msgdef_ntof_mutable(this, name, len); +} +inline const FieldDef* MessageDef::FindFieldByNumber(uint32_t number) const { + return upb_msgdef_itof(this, number); +} +inline const FieldDef *MessageDef::FindFieldByName(const char *name, + size_t len) const { + return upb_msgdef_ntof(this, name, len); +} +inline OneofDef* MessageDef::FindOneofByName(const char* name, size_t len) { + return upb_msgdef_ntoo_mutable(this, name, len); +} +inline const OneofDef* MessageDef::FindOneofByName(const char* name, + size_t len) const { + return upb_msgdef_ntoo(this, name, len); +} +inline MessageDef* MessageDef::Dup(const void *owner) const { + return upb_msgdef_dup(this, owner); +} +inline void MessageDef::setmapentry(bool map_entry) { + upb_msgdef_setmapentry(this, map_entry); +} +inline bool MessageDef::mapentry() const { + return upb_msgdef_mapentry(this); +} +inline MessageDef::field_iterator MessageDef::field_begin() { + return field_iterator(this); +} +inline MessageDef::field_iterator MessageDef::field_end() { + return field_iterator::end(this); +} +inline MessageDef::const_field_iterator MessageDef::field_begin() const { + return const_field_iterator(this); +} +inline MessageDef::const_field_iterator MessageDef::field_end() const { + return const_field_iterator::end(this); +} + +inline MessageDef::oneof_iterator MessageDef::oneof_begin() { + return oneof_iterator(this); +} +inline MessageDef::oneof_iterator MessageDef::oneof_end() { + return oneof_iterator::end(this); +} +inline MessageDef::const_oneof_iterator MessageDef::oneof_begin() const { + return const_oneof_iterator(this); +} +inline MessageDef::const_oneof_iterator MessageDef::oneof_end() const { + return const_oneof_iterator::end(this); +} + +inline MessageDef::field_iterator::field_iterator(MessageDef* md) { + upb_msg_field_begin(&iter_, md); +} +inline MessageDef::field_iterator MessageDef::field_iterator::end( + MessageDef* md) { + MessageDef::field_iterator iter(md); + upb_msg_field_iter_setdone(&iter.iter_); + return iter; +} +inline FieldDef* MessageDef::field_iterator::operator*() const { + return upb_msg_iter_field(&iter_); +} +inline void MessageDef::field_iterator::operator++() { + return upb_msg_field_next(&iter_); +} +inline bool MessageDef::field_iterator::operator==( + const field_iterator &other) const { + return upb_inttable_iter_isequal(&iter_, &other.iter_); +} +inline bool MessageDef::field_iterator::operator!=( + const field_iterator &other) const { + return !(*this == other); +} + +inline MessageDef::const_field_iterator::const_field_iterator( + const MessageDef* md) { + upb_msg_field_begin(&iter_, md); +} +inline MessageDef::const_field_iterator MessageDef::const_field_iterator::end( + const MessageDef *md) { + MessageDef::const_field_iterator iter(md); + upb_msg_field_iter_setdone(&iter.iter_); + return iter; +} +inline const FieldDef* MessageDef::const_field_iterator::operator*() const { + return upb_msg_iter_field(&iter_); +} +inline void MessageDef::const_field_iterator::operator++() { + return upb_msg_field_next(&iter_); +} +inline bool MessageDef::const_field_iterator::operator==( + const const_field_iterator &other) const { + return upb_inttable_iter_isequal(&iter_, &other.iter_); +} +inline bool MessageDef::const_field_iterator::operator!=( + const const_field_iterator &other) const { + return !(*this == other); +} + +inline MessageDef::oneof_iterator::oneof_iterator(MessageDef* md) { + upb_msg_oneof_begin(&iter_, md); +} +inline MessageDef::oneof_iterator MessageDef::oneof_iterator::end( + MessageDef* md) { + MessageDef::oneof_iterator iter(md); + upb_msg_oneof_iter_setdone(&iter.iter_); + return iter; +} +inline OneofDef* MessageDef::oneof_iterator::operator*() const { + return upb_msg_iter_oneof(&iter_); +} +inline void MessageDef::oneof_iterator::operator++() { + return upb_msg_oneof_next(&iter_); +} +inline bool MessageDef::oneof_iterator::operator==( + const oneof_iterator &other) const { + return upb_strtable_iter_isequal(&iter_, &other.iter_); +} +inline bool MessageDef::oneof_iterator::operator!=( + const oneof_iterator &other) const { + return !(*this == other); +} + +inline MessageDef::const_oneof_iterator::const_oneof_iterator( + const MessageDef* md) { + upb_msg_oneof_begin(&iter_, md); +} +inline MessageDef::const_oneof_iterator MessageDef::const_oneof_iterator::end( + const MessageDef *md) { + MessageDef::const_oneof_iterator iter(md); + upb_msg_oneof_iter_setdone(&iter.iter_); + return iter; +} +inline const OneofDef* MessageDef::const_oneof_iterator::operator*() const { + return upb_msg_iter_oneof(&iter_); +} +inline void MessageDef::const_oneof_iterator::operator++() { + return upb_msg_oneof_next(&iter_); +} +inline bool MessageDef::const_oneof_iterator::operator==( + const const_oneof_iterator &other) const { + return upb_strtable_iter_isequal(&iter_, &other.iter_); +} +inline bool MessageDef::const_oneof_iterator::operator!=( + const const_oneof_iterator &other) const { + return !(*this == other); +} + +inline reffed_ptr EnumDef::New() { + upb_enumdef *e = upb_enumdef_new(&e); + return reffed_ptr(e, &e); +} +inline const char* EnumDef::full_name() const { + return upb_enumdef_fullname(this); +} +inline bool EnumDef::set_full_name(const char* fullname, Status* s) { + return upb_enumdef_setfullname(this, fullname, s); +} +inline bool EnumDef::set_full_name(const std::string& fullname, Status* s) { + return upb_enumdef_setfullname(this, upb_safecstr(fullname), s); +} +inline bool EnumDef::Freeze(Status* status) { + return upb_enumdef_freeze(this, status); +} +inline int32_t EnumDef::default_value() const { + return upb_enumdef_default(this); +} +inline bool EnumDef::set_default_value(int32_t val, Status* status) { + return upb_enumdef_setdefault(this, val, status); +} +inline int EnumDef::value_count() const { return upb_enumdef_numvals(this); } +inline bool EnumDef::AddValue(const char* name, int32_t num, Status* status) { + return upb_enumdef_addval(this, name, num, status); +} +inline bool EnumDef::AddValue(const std::string& name, int32_t num, + Status* status) { + return upb_enumdef_addval(this, upb_safecstr(name), num, status); +} +inline bool EnumDef::FindValueByName(const char* name, int32_t *num) const { + return upb_enumdef_ntoiz(this, name, num); +} +inline const char* EnumDef::FindValueByNumber(int32_t num) const { + return upb_enumdef_iton(this, num); +} +inline EnumDef* EnumDef::Dup(const void* owner) const { + return upb_enumdef_dup(this, owner); +} + +inline EnumDef::Iterator::Iterator(const EnumDef* e) { + upb_enum_begin(&iter_, e); +} +inline int32_t EnumDef::Iterator::number() { + return upb_enum_iter_number(&iter_); +} +inline const char* EnumDef::Iterator::name() { + return upb_enum_iter_name(&iter_); +} +inline bool EnumDef::Iterator::Done() { return upb_enum_done(&iter_); } +inline void EnumDef::Iterator::Next() { return upb_enum_next(&iter_); } + +inline reffed_ptr OneofDef::New() { + upb_oneofdef *o = upb_oneofdef_new(&o); + return reffed_ptr(o, &o); +} +inline const char* OneofDef::full_name() const { + return upb_oneofdef_name(this); +} + +inline const MessageDef* OneofDef::containing_type() const { + return upb_oneofdef_containingtype(this); +} +inline const char* OneofDef::name() const { + return upb_oneofdef_name(this); +} +inline bool OneofDef::set_name(const char* name, Status* s) { + return upb_oneofdef_setname(this, name, s); +} +inline int OneofDef::field_count() const { + return upb_oneofdef_numfields(this); +} +inline bool OneofDef::AddField(FieldDef* field, Status* s) { + return upb_oneofdef_addfield(this, field, NULL, s); +} +inline bool OneofDef::AddField(const reffed_ptr& field, Status* s) { + return upb_oneofdef_addfield(this, field.get(), NULL, s); +} +inline const FieldDef* OneofDef::FindFieldByName(const char* name, + size_t len) const { + return upb_oneofdef_ntof(this, name, len); +} +inline const FieldDef* OneofDef::FindFieldByNumber(uint32_t num) const { + return upb_oneofdef_itof(this, num); +} +inline OneofDef::iterator OneofDef::begin() { return iterator(this); } +inline OneofDef::iterator OneofDef::end() { return iterator::end(this); } +inline OneofDef::const_iterator OneofDef::begin() const { + return const_iterator(this); +} +inline OneofDef::const_iterator OneofDef::end() const { + return const_iterator::end(this); +} + +inline OneofDef::iterator::iterator(OneofDef* o) { + upb_oneof_begin(&iter_, o); +} +inline OneofDef::iterator OneofDef::iterator::end(OneofDef* o) { + OneofDef::iterator iter(o); + upb_oneof_iter_setdone(&iter.iter_); + return iter; +} +inline FieldDef* OneofDef::iterator::operator*() const { + return upb_oneof_iter_field(&iter_); +} +inline void OneofDef::iterator::operator++() { return upb_oneof_next(&iter_); } +inline bool OneofDef::iterator::operator==(const iterator &other) const { + return upb_inttable_iter_isequal(&iter_, &other.iter_); +} +inline bool OneofDef::iterator::operator!=(const iterator &other) const { + return !(*this == other); +} + +inline OneofDef::const_iterator::const_iterator(const OneofDef* md) { + upb_oneof_begin(&iter_, md); +} +inline OneofDef::const_iterator OneofDef::const_iterator::end( + const OneofDef *md) { + OneofDef::const_iterator iter(md); + upb_oneof_iter_setdone(&iter.iter_); + return iter; +} +inline const FieldDef* OneofDef::const_iterator::operator*() const { + return upb_msg_iter_field(&iter_); +} +inline void OneofDef::const_iterator::operator++() { + return upb_oneof_next(&iter_); +} +inline bool OneofDef::const_iterator::operator==( + const const_iterator &other) const { + return upb_inttable_iter_isequal(&iter_, &other.iter_); +} +inline bool OneofDef::const_iterator::operator!=( + const const_iterator &other) const { + return !(*this == other); +} + +} /* namespace upb */ +#endif + +#endif /* UPB_DEF_H_ */ +/* +** This file contains definitions of structs that should be considered private +** and NOT stable across versions of upb. +** +** The only reason they are declared here and not in .c files is to allow upb +** and the application (if desired) to embed statically-initialized instances +** of structures like defs. +** +** If you include this file, all guarantees of ABI compatibility go out the +** window! Any code that includes this file needs to recompile against the +** exact same version of upb that they are linking against. +** +** You also need to recompile if you change the value of the UPB_DEBUG_REFS +** flag. +*/ + + +#ifndef UPB_STATICINIT_H_ +#define UPB_STATICINIT_H_ + +#ifdef __cplusplus +/* Because of how we do our typedefs, this header can't be included from C++. */ +#error This file cannot be included from C++ +#endif + +/* upb_refcounted *************************************************************/ + + +/* upb_def ********************************************************************/ + +struct upb_def { + upb_refcounted base; + + const char *fullname; + char type; /* A upb_deftype_t (char to save space) */ + + /* Used as a flag during the def's mutable stage. Must be false unless + * it is currently being used by a function on the stack. This allows + * us to easily determine which defs were passed into the function's + * current invocation. */ + bool came_from_user; +}; + +#define UPB_DEF_INIT(name, type, refs, ref2s) \ + { UPB_REFCOUNT_INIT(refs, ref2s), name, type, false } + + +/* upb_fielddef ***************************************************************/ + +struct upb_fielddef { + upb_def base; + + union { + int64_t sint; + uint64_t uint; + double dbl; + float flt; + void *bytes; + } defaultval; + union { + const upb_msgdef *def; /* If !msg_is_symbolic. */ + char *name; /* If msg_is_symbolic. */ + } msg; + union { + const upb_def *def; /* If !subdef_is_symbolic. */ + char *name; /* If subdef_is_symbolic. */ + } sub; /* The msgdef or enumdef for this field, if upb_hassubdef(f). */ + bool subdef_is_symbolic; + bool msg_is_symbolic; + const upb_oneofdef *oneof; + bool default_is_string; + bool type_is_set_; /* False until type is explicitly set. */ + bool is_extension_; + bool lazy_; + bool packed_; + upb_intfmt_t intfmt; + bool tagdelim; + upb_fieldtype_t type_; + upb_label_t label_; + uint32_t number_; + uint32_t selector_base; /* Used to index into a upb::Handlers table. */ + uint32_t index_; +}; + +#define UPB_FIELDDEF_INIT(label, type, intfmt, tagdelim, is_extension, lazy, \ + packed, name, num, msgdef, subdef, selector_base, \ + index, defaultval, refs, ref2s) \ + { \ + UPB_DEF_INIT(name, UPB_DEF_FIELD, refs, ref2s), defaultval, {msgdef}, \ + {subdef}, NULL, false, false, \ + type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES, true, is_extension, \ + lazy, packed, intfmt, tagdelim, type, label, num, selector_base, index \ + } + + +/* upb_msgdef *****************************************************************/ + +struct upb_msgdef { + upb_def base; + + size_t selector_count; + uint32_t submsg_field_count; + + /* Tables for looking up fields by number and name. */ + upb_inttable itof; /* int to field */ + upb_strtable ntof; /* name to field */ + + /* Tables for looking up oneofs by name. */ + upb_strtable ntoo; /* name to oneof */ + + /* Is this a map-entry message? + * TODO: set this flag properly for static descriptors; regenerate + * descriptor.upb.c. */ + bool map_entry; + + /* TODO(haberman): proper extension ranges (there can be multiple). */ +}; + +/* TODO: also support static initialization of the oneofs table. This will be + * needed if we compile in descriptors that contain oneofs. */ +#define UPB_MSGDEF_INIT(name, selector_count, submsg_field_count, itof, ntof, \ + refs, ref2s) \ + { \ + UPB_DEF_INIT(name, UPB_DEF_MSG, refs, ref2s), selector_count, \ + submsg_field_count, itof, ntof, \ + UPB_EMPTY_STRTABLE_INIT(UPB_CTYPE_PTR), false \ + } + + +/* upb_enumdef ****************************************************************/ + +struct upb_enumdef { + upb_def base; + + upb_strtable ntoi; + upb_inttable iton; + int32_t defaultval; +}; + +#define UPB_ENUMDEF_INIT(name, ntoi, iton, defaultval, refs, ref2s) \ + { UPB_DEF_INIT(name, UPB_DEF_ENUM, refs, ref2s), ntoi, iton, defaultval } + + +/* upb_oneofdef ***************************************************************/ + +struct upb_oneofdef { + upb_def base; + + upb_strtable ntof; + upb_inttable itof; + const upb_msgdef *parent; +}; + +#define UPB_ONEOFDEF_INIT(name, ntof, itof, refs, ref2s) \ + { UPB_DEF_INIT(name, UPB_DEF_ENUM, refs, ref2s), ntof, itof } + + +/* upb_symtab *****************************************************************/ + +struct upb_symtab { + upb_refcounted base; + + upb_strtable symtab; +}; + +#define UPB_SYMTAB_INIT(symtab, refs, ref2s) \ + { UPB_REFCOUNT_INIT(refs, ref2s), symtab } + + +#endif /* UPB_STATICINIT_H_ */ +/* +** upb::Handlers (upb_handlers) +** +** A upb_handlers is like a virtual table for a upb_msgdef. Each field of the +** message can have associated functions that will be called when we are +** parsing or visiting a stream of data. This is similar to how handlers work +** in SAX (the Simple API for XML). +** +** The handlers have no idea where the data is coming from, so a single set of +** handlers could be used with two completely different data sources (for +** example, a parser and a visitor over in-memory objects). This decoupling is +** the most important feature of upb, because it allows parsers and serializers +** to be highly reusable. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ + +#ifndef UPB_HANDLERS_H +#define UPB_HANDLERS_H + + +#ifdef __cplusplus +namespace upb { +class BufferHandle; +class BytesHandler; +class HandlerAttributes; +class Handlers; +template class Handler; +template struct CanonicalType; +} /* namespace upb */ +#endif + +UPB_DECLARE_TYPE(upb::BufferHandle, upb_bufhandle) +UPB_DECLARE_TYPE(upb::BytesHandler, upb_byteshandler) +UPB_DECLARE_TYPE(upb::HandlerAttributes, upb_handlerattr) +UPB_DECLARE_DERIVED_TYPE(upb::Handlers, upb::RefCounted, + upb_handlers, upb_refcounted) + +/* The maximum depth that the handler graph can have. This is a resource limit + * for the C stack since we sometimes need to recursively traverse the graph. + * Cycles are ok; the traversal will stop when it detects a cycle, but we must + * hit the cycle before the maximum depth is reached. + * + * If having a single static limit is too inflexible, we can add another variant + * of Handlers::Freeze that allows specifying this as a parameter. */ +#define UPB_MAX_HANDLER_DEPTH 64 + +/* All the different types of handlers that can be registered. + * Only needed for the advanced functions in upb::Handlers. */ +typedef enum { + UPB_HANDLER_INT32, + UPB_HANDLER_INT64, + UPB_HANDLER_UINT32, + UPB_HANDLER_UINT64, + UPB_HANDLER_FLOAT, + UPB_HANDLER_DOUBLE, + UPB_HANDLER_BOOL, + UPB_HANDLER_STARTSTR, + UPB_HANDLER_STRING, + UPB_HANDLER_ENDSTR, + UPB_HANDLER_STARTSUBMSG, + UPB_HANDLER_ENDSUBMSG, + UPB_HANDLER_STARTSEQ, + UPB_HANDLER_ENDSEQ +} upb_handlertype_t; + +#define UPB_HANDLER_MAX (UPB_HANDLER_ENDSEQ+1) + +#define UPB_BREAK NULL + +/* A convenient definition for when no closure is needed. */ +extern char _upb_noclosure; +#define UPB_NO_CLOSURE &_upb_noclosure + +/* A selector refers to a specific field handler in the Handlers object + * (for example: the STARTSUBMSG handler for field "field15"). */ +typedef int32_t upb_selector_t; + +UPB_BEGIN_EXTERN_C + +/* Forward-declares for C inline accessors. We need to declare these here + * so we can "friend" them in the class declarations in C++. */ +UPB_INLINE upb_func *upb_handlers_gethandler(const upb_handlers *h, + upb_selector_t s); +UPB_INLINE const void *upb_handlerattr_handlerdata(const upb_handlerattr *attr); +UPB_INLINE const void *upb_handlers_gethandlerdata(const upb_handlers *h, + upb_selector_t s); + +UPB_INLINE void upb_bufhandle_init(upb_bufhandle *h); +UPB_INLINE void upb_bufhandle_setobj(upb_bufhandle *h, const void *obj, + const void *type); +UPB_INLINE void upb_bufhandle_setbuf(upb_bufhandle *h, const char *buf, + size_t ofs); +UPB_INLINE const void *upb_bufhandle_obj(const upb_bufhandle *h); +UPB_INLINE const void *upb_bufhandle_objtype(const upb_bufhandle *h); +UPB_INLINE const char *upb_bufhandle_buf(const upb_bufhandle *h); + +UPB_END_EXTERN_C + + +/* Static selectors for upb::Handlers. */ +#define UPB_STARTMSG_SELECTOR 0 +#define UPB_ENDMSG_SELECTOR 1 +#define UPB_STATIC_SELECTOR_COUNT 2 + +/* Static selectors for upb::BytesHandler. */ +#define UPB_STARTSTR_SELECTOR 0 +#define UPB_STRING_SELECTOR 1 +#define UPB_ENDSTR_SELECTOR 2 + +typedef void upb_handlerfree(void *d); + +#ifdef __cplusplus + +/* A set of attributes that accompanies a handler's function pointer. */ +class upb::HandlerAttributes { + public: + HandlerAttributes(); + ~HandlerAttributes(); + + /* Sets the handler data that will be passed as the second parameter of the + * handler. To free this pointer when the handlers are freed, call + * Handlers::AddCleanup(). */ + bool SetHandlerData(const void *handler_data); + const void* handler_data() const; + + /* Use this to specify the type of the closure. This will be checked against + * all other closure types for handler that use the same closure. + * Registration will fail if this does not match all other non-NULL closure + * types. */ + bool SetClosureType(const void *closure_type); + const void* closure_type() const; + + /* Use this to specify the type of the returned closure. Only used for + * Start*{String,SubMessage,Sequence} handlers. This must match the closure + * type of any handlers that use it (for example, the StringBuf handler must + * match the closure returned from StartString). */ + bool SetReturnClosureType(const void *return_closure_type); + const void* return_closure_type() const; + + /* Set to indicate that the handler always returns "ok" (either "true" or a + * non-NULL closure). This is a hint that can allow code generators to + * generate more efficient code. */ + bool SetAlwaysOk(bool always_ok); + bool always_ok() const; + + private: + friend UPB_INLINE const void * ::upb_handlerattr_handlerdata( + const upb_handlerattr *attr); +#else +struct upb_handlerattr { +#endif + const void *handler_data_; + const void *closure_type_; + const void *return_closure_type_; + bool alwaysok_; +}; + +#define UPB_HANDLERATTR_INITIALIZER {NULL, NULL, NULL, false} + +typedef struct { + upb_func *func; + + /* It is wasteful to include the entire attributes here: + * + * * Some of the information is redundant (like storing the closure type + * separately for each handler that must match). + * * Some of the info is only needed prior to freeze() (like closure types). + * * alignment padding wastes a lot of space for alwaysok_. + * + * If/when the size and locality of handlers is an issue, we can optimize this + * not to store the entire attr like this. We do not expose the table's + * layout to allow this optimization in the future. */ + upb_handlerattr attr; +} upb_handlers_tabent; + +#ifdef __cplusplus + +/* Extra information about a buffer that is passed to a StringBuf handler. + * TODO(haberman): allow the handle to be pinned so that it will outlive + * the handler invocation. */ +class upb::BufferHandle { + public: + BufferHandle(); + ~BufferHandle(); + + /* The beginning of the buffer. This may be different than the pointer + * passed to a StringBuf handler because the handler may receive data + * that is from the middle or end of a larger buffer. */ + const char* buffer() const; + + /* The offset within the attached object where this buffer begins. Only + * meaningful if there is an attached object. */ + size_t object_offset() const; + + /* Note that object_offset is the offset of "buf" within the attached + * object. */ + void SetBuffer(const char* buf, size_t object_offset); + + /* The BufferHandle can have an "attached object", which can be used to + * tunnel through a pointer to the buffer's underlying representation. */ + template + void SetAttachedObject(const T* obj); + + /* Returns NULL if the attached object is not of this type. */ + template + const T* GetAttachedObject() const; + + private: + friend UPB_INLINE void ::upb_bufhandle_init(upb_bufhandle *h); + friend UPB_INLINE void ::upb_bufhandle_setobj(upb_bufhandle *h, + const void *obj, + const void *type); + friend UPB_INLINE void ::upb_bufhandle_setbuf(upb_bufhandle *h, + const char *buf, size_t ofs); + friend UPB_INLINE const void* ::upb_bufhandle_obj(const upb_bufhandle *h); + friend UPB_INLINE const void* ::upb_bufhandle_objtype( + const upb_bufhandle *h); + friend UPB_INLINE const char* ::upb_bufhandle_buf(const upb_bufhandle *h); +#else +struct upb_bufhandle { +#endif + const char *buf_; + const void *obj_; + const void *objtype_; + size_t objofs_; +}; + +#ifdef __cplusplus + +/* A upb::Handlers object represents the set of handlers associated with a + * message in the graph of messages. You can think of it as a big virtual + * table with functions corresponding to all the events that can fire while + * parsing or visiting a message of a specific type. + * + * Any handlers that are not set behave as if they had successfully consumed + * the value. Any unset Start* handlers will propagate their closure to the + * inner frame. + * + * The easiest way to create the *Handler objects needed by the Set* methods is + * with the UpbBind() and UpbMakeHandler() macros; see below. */ +class upb::Handlers { + public: + typedef upb_selector_t Selector; + typedef upb_handlertype_t Type; + + typedef Handler StartFieldHandler; + typedef Handler EndFieldHandler; + typedef Handler StartMessageHandler; + typedef Handler EndMessageHandler; + typedef Handler StartStringHandler; + typedef Handler StringHandler; + + template struct ValueHandler { + typedef Handler H; + }; + + typedef ValueHandler::H Int32Handler; + typedef ValueHandler::H Int64Handler; + typedef ValueHandler::H UInt32Handler; + typedef ValueHandler::H UInt64Handler; + typedef ValueHandler::H FloatHandler; + typedef ValueHandler::H DoubleHandler; + typedef ValueHandler::H BoolHandler; + + /* Any function pointer can be converted to this and converted back to its + * correct type. */ + typedef void GenericFunction(); + + typedef void HandlersCallback(const void *closure, upb_handlers *h); + + /* Returns a new handlers object for the given frozen msgdef. + * Returns NULL if memory allocation failed. */ + static reffed_ptr New(const MessageDef *m); + + /* Convenience function for registering a graph of handlers that mirrors the + * graph of msgdefs for some message. For "m" and all its children a new set + * of handlers will be created and the given callback will be invoked, + * allowing the client to register handlers for this message. Note that any + * subhandlers set by the callback will be overwritten. */ + static reffed_ptr NewFrozen(const MessageDef *m, + HandlersCallback *callback, + const void *closure); + + /* Functionality from upb::RefCounted. */ + UPB_REFCOUNTED_CPPMETHODS + + /* All handler registration functions return bool to indicate success or + * failure; details about failures are stored in this status object. If a + * failure does occur, it must be cleared before the Handlers are frozen, + * otherwise the freeze() operation will fail. The functions may *only* be + * used while the Handlers are mutable. */ + const Status* status(); + void ClearError(); + + /* Call to freeze these Handlers. Requires that any SubHandlers are already + * frozen. For cycles, you must use the static version below and freeze the + * whole graph at once. */ + bool Freeze(Status* s); + + /* Freezes the given set of handlers. You may not freeze a handler without + * also freezing any handlers they point to. */ + static bool Freeze(Handlers*const* handlers, int n, Status* s); + static bool Freeze(const std::vector& handlers, Status* s); + + /* Returns the msgdef associated with this handlers object. */ + const MessageDef* message_def() const; + + /* Adds the given pointer and function to the list of cleanup functions that + * will be run when these handlers are freed. If this pointer has previously + * been registered, the function returns false and does nothing. */ + bool AddCleanup(void *ptr, upb_handlerfree *cleanup); + + /* Sets the startmsg handler for the message, which is defined as follows: + * + * bool startmsg(MyType* closure) { + * // Called when the message begins. Returns true if processing should + * // continue. + * return true; + * } + */ + bool SetStartMessageHandler(const StartMessageHandler& handler); + + /* Sets the endmsg handler for the message, which is defined as follows: + * + * bool endmsg(MyType* closure, upb_status *status) { + * // Called when processing of this message ends, whether in success or + * // failure. "status" indicates the final status of processing, and + * // can also be modified in-place to update the final status. + * } + */ + bool SetEndMessageHandler(const EndMessageHandler& handler); + + /* Sets the value handler for the given field, which is defined as follows + * (this is for an int32 field; other field types will pass their native + * C/C++ type for "val"): + * + * bool OnValue(MyClosure* c, const MyHandlerData* d, int32_t val) { + * // Called when the field's value is encountered. "d" contains + * // whatever data was bound to this field when it was registered. + * // Returns true if processing should continue. + * return true; + * } + * + * handers->SetInt32Handler(f, UpbBind(OnValue, new MyHandlerData(...))); + * + * The value type must exactly match f->type(). + * For example, a handler that takes an int32_t parameter may only be used for + * fields of type UPB_TYPE_INT32 and UPB_TYPE_ENUM. + * + * Returns false if the handler failed to register; in this case the cleanup + * handler (if any) will be called immediately. + */ + bool SetInt32Handler (const FieldDef* f, const Int32Handler& h); + bool SetInt64Handler (const FieldDef* f, const Int64Handler& h); + bool SetUInt32Handler(const FieldDef* f, const UInt32Handler& h); + bool SetUInt64Handler(const FieldDef* f, const UInt64Handler& h); + bool SetFloatHandler (const FieldDef* f, const FloatHandler& h); + bool SetDoubleHandler(const FieldDef* f, const DoubleHandler& h); + bool SetBoolHandler (const FieldDef* f, const BoolHandler& h); + + /* Like the previous, but templated on the type on the value (ie. int32). + * This is mostly useful to call from other templates. To call this you must + * specify the template parameter explicitly, ie: + * h->SetValueHandler(f, UpbBind(MyHandler, MyData)); */ + template + bool SetValueHandler( + const FieldDef *f, + const typename ValueHandler::Type>::H& handler); + + /* Sets handlers for a string field, which are defined as follows: + * + * MySubClosure* startstr(MyClosure* c, const MyHandlerData* d, + * size_t size_hint) { + * // Called when a string value begins. The return value indicates the + * // closure for the string. "size_hint" indicates the size of the + * // string if it is known, however if the string is length-delimited + * // and the end-of-string is not available size_hint will be zero. + * // This case is indistinguishable from the case where the size is + * // known to be zero. + * // + * // TODO(haberman): is it important to distinguish these cases? + * // If we had ssize_t as a type we could make -1 "unknown", but + * // ssize_t is POSIX (not ANSI) and therefore less portable. + * // In practice I suspect it won't be important to distinguish. + * return closure; + * } + * + * size_t str(MyClosure* closure, const MyHandlerData* d, + * const char *str, size_t len) { + * // Called for each buffer of string data; the multiple physical buffers + * // are all part of the same logical string. The return value indicates + * // how many bytes were consumed. If this number is less than "len", + * // this will also indicate that processing should be halted for now, + * // like returning false or UPB_BREAK from any other callback. If + * // number is greater than "len", the excess bytes will be skipped over + * // and not passed to the callback. + * return len; + * } + * + * bool endstr(MyClosure* c, const MyHandlerData* d) { + * // Called when a string value ends. Return value indicates whether + * // processing should continue. + * return true; + * } + */ + bool SetStartStringHandler(const FieldDef* f, const StartStringHandler& h); + bool SetStringHandler(const FieldDef* f, const StringHandler& h); + bool SetEndStringHandler(const FieldDef* f, const EndFieldHandler& h); + + /* Sets the startseq handler, which is defined as follows: + * + * MySubClosure *startseq(MyClosure* c, const MyHandlerData* d) { + * // Called when a sequence (repeated field) begins. The returned + * // pointer indicates the closure for the sequence (or UPB_BREAK + * // to interrupt processing). + * return closure; + * } + * + * h->SetStartSequenceHandler(f, UpbBind(startseq, new MyHandlerData(...))); + * + * Returns "false" if "f" does not belong to this message or is not a + * repeated field. + */ + bool SetStartSequenceHandler(const FieldDef* f, const StartFieldHandler& h); + + /* Sets the startsubmsg handler for the given field, which is defined as + * follows: + * + * MySubClosure* startsubmsg(MyClosure* c, const MyHandlerData* d) { + * // Called when a submessage begins. The returned pointer indicates the + * // closure for the sequence (or UPB_BREAK to interrupt processing). + * return closure; + * } + * + * h->SetStartSubMessageHandler(f, UpbBind(startsubmsg, + * new MyHandlerData(...))); + * + * Returns "false" if "f" does not belong to this message or is not a + * submessage/group field. + */ + bool SetStartSubMessageHandler(const FieldDef* f, const StartFieldHandler& h); + + /* Sets the endsubmsg handler for the given field, which is defined as + * follows: + * + * bool endsubmsg(MyClosure* c, const MyHandlerData* d) { + * // Called when a submessage ends. Returns true to continue processing. + * return true; + * } + * + * Returns "false" if "f" does not belong to this message or is not a + * submessage/group field. + */ + bool SetEndSubMessageHandler(const FieldDef *f, const EndFieldHandler &h); + + /* Starts the endsubseq handler for the given field, which is defined as + * follows: + * + * bool endseq(MyClosure* c, const MyHandlerData* d) { + * // Called when a sequence ends. Returns true continue processing. + * return true; + * } + * + * Returns "false" if "f" does not belong to this message or is not a + * repeated field. + */ + bool SetEndSequenceHandler(const FieldDef* f, const EndFieldHandler& h); + + /* Sets or gets the object that specifies handlers for the given field, which + * must be a submessage or group. Returns NULL if no handlers are set. */ + bool SetSubHandlers(const FieldDef* f, const Handlers* sub); + const Handlers* GetSubHandlers(const FieldDef* f) const; + + /* Equivalent to GetSubHandlers, but takes the STARTSUBMSG selector for the + * field. */ + const Handlers* GetSubHandlers(Selector startsubmsg) const; + + /* A selector refers to a specific field handler in the Handlers object + * (for example: the STARTSUBMSG handler for field "field15"). + * On success, returns true and stores the selector in "s". + * If the FieldDef or Type are invalid, returns false. + * The returned selector is ONLY valid for Handlers whose MessageDef + * contains this FieldDef. */ + static bool GetSelector(const FieldDef* f, Type type, Selector* s); + + /* Given a START selector of any kind, returns the corresponding END selector. */ + static Selector GetEndSelector(Selector start_selector); + + /* Returns the function pointer for this handler. It is the client's + * responsibility to cast to the correct function type before calling it. */ + GenericFunction* GetHandler(Selector selector); + + /* Sets the given attributes to the attributes for this selector. */ + bool GetAttributes(Selector selector, HandlerAttributes* attr); + + /* Returns the handler data that was registered with this handler. */ + const void* GetHandlerData(Selector selector); + + /* Could add any of the following functions as-needed, with some minor + * implementation changes: + * + * const FieldDef* GetFieldDef(Selector selector); + * static bool IsSequence(Selector selector); */ + + private: + UPB_DISALLOW_POD_OPS(Handlers, upb::Handlers) + + friend UPB_INLINE GenericFunction *::upb_handlers_gethandler( + const upb_handlers *h, upb_selector_t s); + friend UPB_INLINE const void *::upb_handlers_gethandlerdata( + const upb_handlers *h, upb_selector_t s); +#else +struct upb_handlers { +#endif + upb_refcounted base; + + const upb_msgdef *msg; + const upb_handlers **sub; + const void *top_closure_type; + upb_inttable cleanup_; + upb_status status_; /* Used only when mutable. */ + upb_handlers_tabent table[1]; /* Dynamically-sized field handler array. */ +}; + +#ifdef __cplusplus + +namespace upb { + +/* Convenience macros for creating a Handler object that is wrapped with a + * type-safe wrapper function that converts the "void*" parameters/returns + * of the underlying C API into nice C++ function. + * + * Sample usage: + * void OnValue1(MyClosure* c, const MyHandlerData* d, int32_t val) { + * // do stuff ... + * } + * + * // Handler that doesn't need any data bound to it. + * void OnValue2(MyClosure* c, int32_t val) { + * // do stuff ... + * } + * + * // Handler that returns bool so it can return failure if necessary. + * bool OnValue3(MyClosure* c, int32_t val) { + * // do stuff ... + * return ok; + * } + * + * // Member function handler. + * class MyClosure { + * public: + * void OnValue(int32_t val) { + * // do stuff ... + * } + * }; + * + * // Takes ownership of the MyHandlerData. + * handlers->SetInt32Handler(f1, UpbBind(OnValue1, new MyHandlerData(...))); + * handlers->SetInt32Handler(f2, UpbMakeHandler(OnValue2)); + * handlers->SetInt32Handler(f1, UpbMakeHandler(OnValue3)); + * handlers->SetInt32Handler(f2, UpbMakeHandler(&MyClosure::OnValue)); + */ + +#ifdef UPB_CXX11 + +/* In C++11, the "template" disambiguator can appear even outside templates, + * so all calls can safely use this pair of macros. */ + +#define UpbMakeHandler(f) upb::MatchFunc(f).template GetFunc() + +/* We have to be careful to only evaluate "d" once. */ +#define UpbBind(f, d) upb::MatchFunc(f).template GetFunc((d)) + +#else + +/* Prior to C++11, the "template" disambiguator may only appear inside a + * template, so the regular macro must not use "template" */ + +#define UpbMakeHandler(f) upb::MatchFunc(f).GetFunc() + +#define UpbBind(f, d) upb::MatchFunc(f).GetFunc((d)) + +#endif /* UPB_CXX11 */ + +/* This macro must be used in C++98 for calls from inside a template. But we + * define this variant in all cases; code that wants to be compatible with both + * C++98 and C++11 should always use this macro when calling from a template. */ +#define UpbMakeHandlerT(f) upb::MatchFunc(f).template GetFunc() + +/* We have to be careful to only evaluate "d" once. */ +#define UpbBindT(f, d) upb::MatchFunc(f).template GetFunc((d)) + +/* Handler: a struct that contains the (handler, data, deleter) tuple that is + * used to register all handlers. Users can Make() these directly but it's + * more convenient to use the UpbMakeHandler/UpbBind macros above. */ +template class Handler { + public: + /* The underlying, handler function signature that upb uses internally. */ + typedef T FuncPtr; + + /* Intentionally implicit. */ + template Handler(F func); + ~Handler(); + + private: + void AddCleanup(Handlers* h) const { + if (cleanup_func_) { + bool ok = h->AddCleanup(cleanup_data_, cleanup_func_); + UPB_ASSERT_VAR(ok, ok); + } + } + + UPB_DISALLOW_COPY_AND_ASSIGN(Handler) + friend class Handlers; + FuncPtr handler_; + mutable HandlerAttributes attr_; + mutable bool registered_; + void *cleanup_data_; + upb_handlerfree *cleanup_func_; +}; + +} /* namespace upb */ + +#endif /* __cplusplus */ + +UPB_BEGIN_EXTERN_C + +/* Native C API. */ + +/* Handler function typedefs. */ +typedef bool upb_startmsg_handlerfunc(void *c, const void*); +typedef bool upb_endmsg_handlerfunc(void *c, const void *, upb_status *status); +typedef void* upb_startfield_handlerfunc(void *c, const void *hd); +typedef bool upb_endfield_handlerfunc(void *c, const void *hd); +typedef bool upb_int32_handlerfunc(void *c, const void *hd, int32_t val); +typedef bool upb_int64_handlerfunc(void *c, const void *hd, int64_t val); +typedef bool upb_uint32_handlerfunc(void *c, const void *hd, uint32_t val); +typedef bool upb_uint64_handlerfunc(void *c, const void *hd, uint64_t val); +typedef bool upb_float_handlerfunc(void *c, const void *hd, float val); +typedef bool upb_double_handlerfunc(void *c, const void *hd, double val); +typedef bool upb_bool_handlerfunc(void *c, const void *hd, bool val); +typedef void *upb_startstr_handlerfunc(void *c, const void *hd, + size_t size_hint); +typedef size_t upb_string_handlerfunc(void *c, const void *hd, const char *buf, + size_t n, const upb_bufhandle* handle); + +/* upb_bufhandle */ +size_t upb_bufhandle_objofs(const upb_bufhandle *h); + +/* upb_handlerattr */ +void upb_handlerattr_init(upb_handlerattr *attr); +void upb_handlerattr_uninit(upb_handlerattr *attr); + +bool upb_handlerattr_sethandlerdata(upb_handlerattr *attr, const void *hd); +bool upb_handlerattr_setclosuretype(upb_handlerattr *attr, const void *type); +const void *upb_handlerattr_closuretype(const upb_handlerattr *attr); +bool upb_handlerattr_setreturnclosuretype(upb_handlerattr *attr, + const void *type); +const void *upb_handlerattr_returnclosuretype(const upb_handlerattr *attr); +bool upb_handlerattr_setalwaysok(upb_handlerattr *attr, bool alwaysok); +bool upb_handlerattr_alwaysok(const upb_handlerattr *attr); + +UPB_INLINE const void *upb_handlerattr_handlerdata( + const upb_handlerattr *attr) { + return attr->handler_data_; +} + +/* upb_handlers */ +typedef void upb_handlers_callback(const void *closure, upb_handlers *h); +upb_handlers *upb_handlers_new(const upb_msgdef *m, + const void *owner); +const upb_handlers *upb_handlers_newfrozen(const upb_msgdef *m, + const void *owner, + upb_handlers_callback *callback, + const void *closure); + +/* Include refcounted methods like upb_handlers_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_handlers, upb_handlers_upcast) + +const upb_status *upb_handlers_status(upb_handlers *h); +void upb_handlers_clearerr(upb_handlers *h); +const upb_msgdef *upb_handlers_msgdef(const upb_handlers *h); +bool upb_handlers_addcleanup(upb_handlers *h, void *p, upb_handlerfree *hfree); + +bool upb_handlers_setstartmsg(upb_handlers *h, upb_startmsg_handlerfunc *func, + upb_handlerattr *attr); +bool upb_handlers_setendmsg(upb_handlers *h, upb_endmsg_handlerfunc *func, + upb_handlerattr *attr); +bool upb_handlers_setint32(upb_handlers *h, const upb_fielddef *f, + upb_int32_handlerfunc *func, upb_handlerattr *attr); +bool upb_handlers_setint64(upb_handlers *h, const upb_fielddef *f, + upb_int64_handlerfunc *func, upb_handlerattr *attr); +bool upb_handlers_setuint32(upb_handlers *h, const upb_fielddef *f, + upb_uint32_handlerfunc *func, + upb_handlerattr *attr); +bool upb_handlers_setuint64(upb_handlers *h, const upb_fielddef *f, + upb_uint64_handlerfunc *func, + upb_handlerattr *attr); +bool upb_handlers_setfloat(upb_handlers *h, const upb_fielddef *f, + upb_float_handlerfunc *func, upb_handlerattr *attr); +bool upb_handlers_setdouble(upb_handlers *h, const upb_fielddef *f, + upb_double_handlerfunc *func, + upb_handlerattr *attr); +bool upb_handlers_setbool(upb_handlers *h, const upb_fielddef *f, + upb_bool_handlerfunc *func, + upb_handlerattr *attr); +bool upb_handlers_setstartstr(upb_handlers *h, const upb_fielddef *f, + upb_startstr_handlerfunc *func, + upb_handlerattr *attr); +bool upb_handlers_setstring(upb_handlers *h, const upb_fielddef *f, + upb_string_handlerfunc *func, + upb_handlerattr *attr); +bool upb_handlers_setendstr(upb_handlers *h, const upb_fielddef *f, + upb_endfield_handlerfunc *func, + upb_handlerattr *attr); +bool upb_handlers_setstartseq(upb_handlers *h, const upb_fielddef *f, + upb_startfield_handlerfunc *func, + upb_handlerattr *attr); +bool upb_handlers_setstartsubmsg(upb_handlers *h, const upb_fielddef *f, + upb_startfield_handlerfunc *func, + upb_handlerattr *attr); +bool upb_handlers_setendsubmsg(upb_handlers *h, const upb_fielddef *f, + upb_endfield_handlerfunc *func, + upb_handlerattr *attr); +bool upb_handlers_setendseq(upb_handlers *h, const upb_fielddef *f, + upb_endfield_handlerfunc *func, + upb_handlerattr *attr); + +bool upb_handlers_setsubhandlers(upb_handlers *h, const upb_fielddef *f, + const upb_handlers *sub); +const upb_handlers *upb_handlers_getsubhandlers(const upb_handlers *h, + const upb_fielddef *f); +const upb_handlers *upb_handlers_getsubhandlers_sel(const upb_handlers *h, + upb_selector_t sel); + +UPB_INLINE upb_func *upb_handlers_gethandler(const upb_handlers *h, + upb_selector_t s) { + return (upb_func *)h->table[s].func; +} + +bool upb_handlers_getattr(const upb_handlers *h, upb_selector_t s, + upb_handlerattr *attr); + +UPB_INLINE const void *upb_handlers_gethandlerdata(const upb_handlers *h, + upb_selector_t s) { + return upb_handlerattr_handlerdata(&h->table[s].attr); +} + +#ifdef __cplusplus + +/* Handler types for single fields. + * Right now we only have one for TYPE_BYTES but ones for other types + * should follow. + * + * These follow the same handlers protocol for fields of a message. */ +class upb::BytesHandler { + public: + BytesHandler(); + ~BytesHandler(); +#else +struct upb_byteshandler { +#endif + upb_handlers_tabent table[3]; +}; + +void upb_byteshandler_init(upb_byteshandler *h); + +/* Caller must ensure that "d" outlives the handlers. + * TODO(haberman): should this have a "freeze" operation? It's not necessary + * for memory management, but could be useful to force immutability and provide + * a convenient moment to verify that all registration succeeded. */ +bool upb_byteshandler_setstartstr(upb_byteshandler *h, + upb_startstr_handlerfunc *func, void *d); +bool upb_byteshandler_setstring(upb_byteshandler *h, + upb_string_handlerfunc *func, void *d); +bool upb_byteshandler_setendstr(upb_byteshandler *h, + upb_endfield_handlerfunc *func, void *d); + +/* "Static" methods */ +bool upb_handlers_freeze(upb_handlers *const *handlers, int n, upb_status *s); +upb_handlertype_t upb_handlers_getprimitivehandlertype(const upb_fielddef *f); +bool upb_handlers_getselector(const upb_fielddef *f, upb_handlertype_t type, + upb_selector_t *s); +UPB_INLINE upb_selector_t upb_handlers_getendselector(upb_selector_t start) { + return start + 1; +} + +/* Internal-only. */ +uint32_t upb_handlers_selectorbaseoffset(const upb_fielddef *f); +uint32_t upb_handlers_selectorcount(const upb_fielddef *f); + +UPB_END_EXTERN_C + +/* +** Inline definitions for handlers.h, which are particularly long and a bit +** tricky. +*/ + +#ifndef UPB_HANDLERS_INL_H_ +#define UPB_HANDLERS_INL_H_ + +#include + +/* C inline methods. */ + +/* upb_bufhandle */ +UPB_INLINE void upb_bufhandle_init(upb_bufhandle *h) { + h->obj_ = NULL; + h->objtype_ = NULL; + h->buf_ = NULL; + h->objofs_ = 0; +} +UPB_INLINE void upb_bufhandle_uninit(upb_bufhandle *h) { + UPB_UNUSED(h); +} +UPB_INLINE void upb_bufhandle_setobj(upb_bufhandle *h, const void *obj, + const void *type) { + h->obj_ = obj; + h->objtype_ = type; +} +UPB_INLINE void upb_bufhandle_setbuf(upb_bufhandle *h, const char *buf, + size_t ofs) { + h->buf_ = buf; + h->objofs_ = ofs; +} +UPB_INLINE const void *upb_bufhandle_obj(const upb_bufhandle *h) { + return h->obj_; +} +UPB_INLINE const void *upb_bufhandle_objtype(const upb_bufhandle *h) { + return h->objtype_; +} +UPB_INLINE const char *upb_bufhandle_buf(const upb_bufhandle *h) { + return h->buf_; +} + + +#ifdef __cplusplus + +/* Type detection and typedefs for integer types. + * For platforms where there are multiple 32-bit or 64-bit types, we need to be + * able to enumerate them so we can properly create overloads for all variants. + * + * If any platform existed where there were three integer types with the same + * size, this would have to become more complicated. For example, short, int, + * and long could all be 32-bits. Even more diabolically, short, int, long, + * and long long could all be 64 bits and still be standard-compliant. + * However, few platforms are this strange, and it's unlikely that upb will be + * used on the strangest ones. */ + +/* Can't count on stdint.h limits like INT32_MAX, because in C++ these are + * only defined when __STDC_LIMIT_MACROS are defined before the *first* include + * of stdint.h. We can't guarantee that someone else didn't include these first + * without defining __STDC_LIMIT_MACROS. */ +#define UPB_INT32_MAX 0x7fffffffLL +#define UPB_INT32_MIN (-UPB_INT32_MAX - 1) +#define UPB_INT64_MAX 0x7fffffffffffffffLL +#define UPB_INT64_MIN (-UPB_INT64_MAX - 1) + +#if INT_MAX == UPB_INT32_MAX && INT_MIN == UPB_INT32_MIN +#define UPB_INT_IS_32BITS 1 +#endif + +#if LONG_MAX == UPB_INT32_MAX && LONG_MIN == UPB_INT32_MIN +#define UPB_LONG_IS_32BITS 1 +#endif + +#if LONG_MAX == UPB_INT64_MAX && LONG_MIN == UPB_INT64_MIN +#define UPB_LONG_IS_64BITS 1 +#endif + +#if LLONG_MAX == UPB_INT64_MAX && LLONG_MIN == UPB_INT64_MIN +#define UPB_LLONG_IS_64BITS 1 +#endif + +/* We use macros instead of typedefs so we can undefine them later and avoid + * leaking them outside this header file. */ +#if UPB_INT_IS_32BITS +#define UPB_INT32_T int +#define UPB_UINT32_T unsigned int + +#if UPB_LONG_IS_32BITS +#define UPB_TWO_32BIT_TYPES 1 +#define UPB_INT32ALT_T long +#define UPB_UINT32ALT_T unsigned long +#endif /* UPB_LONG_IS_32BITS */ + +#elif UPB_LONG_IS_32BITS /* && !UPB_INT_IS_32BITS */ +#define UPB_INT32_T long +#define UPB_UINT32_T unsigned long +#endif /* UPB_INT_IS_32BITS */ + + +#if UPB_LONG_IS_64BITS +#define UPB_INT64_T long +#define UPB_UINT64_T unsigned long + +#if UPB_LLONG_IS_64BITS +#define UPB_TWO_64BIT_TYPES 1 +#define UPB_INT64ALT_T long long +#define UPB_UINT64ALT_T unsigned long long +#endif /* UPB_LLONG_IS_64BITS */ + +#elif UPB_LLONG_IS_64BITS /* && !UPB_LONG_IS_64BITS */ +#define UPB_INT64_T long long +#define UPB_UINT64_T unsigned long long +#endif /* UPB_LONG_IS_64BITS */ + +#undef UPB_INT32_MAX +#undef UPB_INT32_MIN +#undef UPB_INT64_MAX +#undef UPB_INT64_MIN +#undef UPB_INT_IS_32BITS +#undef UPB_LONG_IS_32BITS +#undef UPB_LONG_IS_64BITS +#undef UPB_LLONG_IS_64BITS + + +namespace upb { + +typedef void CleanupFunc(void *ptr); + +/* Template to remove "const" from "const T*" and just return "T*". + * + * We define a nonsense default because otherwise it will fail to instantiate as + * a function parameter type even in cases where we don't expect any caller to + * actually match the overload. */ +class CouldntRemoveConst {}; +template struct remove_constptr { typedef CouldntRemoveConst type; }; +template struct remove_constptr { typedef T *type; }; + +/* Template that we use below to remove a template specialization from + * consideration if it matches a specific type. */ +template struct disable_if_same { typedef void Type; }; +template struct disable_if_same {}; + +template void DeletePointer(void *p) { delete static_cast(p); } + +template +struct FirstUnlessVoidOrBool { + typedef T1 value; +}; + +template +struct FirstUnlessVoidOrBool { + typedef T2 value; +}; + +template +struct FirstUnlessVoidOrBool { + typedef T2 value; +}; + +template +struct is_same { + static bool value; +}; + +template +struct is_same { + static bool value; +}; + +template +bool is_same::value = false; + +template +bool is_same::value = true; + +/* FuncInfo *******************************************************************/ + +/* Info about the user's original, pre-wrapped function. */ +template +struct FuncInfo { + /* The type of the closure that the function takes (its first param). */ + typedef C Closure; + + /* The return type. */ + typedef R Return; +}; + +/* Func ***********************************************************************/ + +/* Func1, Func2, Func3: Template classes representing a function and its + * signature. + * + * Since the function is a template parameter, calling the function can be + * inlined at compile-time and does not require a function pointer at runtime. + * These functions are not bound to a handler data so have no data or cleanup + * handler. */ +struct UnboundFunc { + CleanupFunc *GetCleanup() { return NULL; } + void *GetData() { return NULL; } +}; + +template +struct Func1 : public UnboundFunc { + typedef R Return; + typedef I FuncInfo; + static R Call(P1 p1) { return F(p1); } +}; + +template +struct Func2 : public UnboundFunc { + typedef R Return; + typedef I FuncInfo; + static R Call(P1 p1, P2 p2) { return F(p1, p2); } +}; + +template +struct Func3 : public UnboundFunc { + typedef R Return; + typedef I FuncInfo; + static R Call(P1 p1, P2 p2, P3 p3) { return F(p1, p2, p3); } +}; + +template +struct Func4 : public UnboundFunc { + typedef R Return; + typedef I FuncInfo; + static R Call(P1 p1, P2 p2, P3 p3, P4 p4) { return F(p1, p2, p3, p4); } +}; + +template +struct Func5 : public UnboundFunc { + typedef R Return; + typedef I FuncInfo; + static R Call(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) { + return F(p1, p2, p3, p4, p5); + } +}; + +/* BoundFunc ******************************************************************/ + +/* BoundFunc2, BoundFunc3: Like Func2/Func3 except also contains a value that + * shall be bound to the function's second parameter. + * + * Note that the second parameter is a const pointer, but our stored bound value + * is non-const so we can free it when the handlers are destroyed. */ +template +struct BoundFunc { + typedef typename remove_constptr::type MutableP2; + explicit BoundFunc(MutableP2 data_) : data(data_) {} + CleanupFunc *GetCleanup() { return &DeletePointer; } + MutableP2 GetData() { return data; } + MutableP2 data; +}; + +template +struct BoundFunc2 : public BoundFunc { + typedef BoundFunc Base; + typedef I FuncInfo; + explicit BoundFunc2(typename Base::MutableP2 arg) : Base(arg) {} +}; + +template +struct BoundFunc3 : public BoundFunc { + typedef BoundFunc Base; + typedef I FuncInfo; + explicit BoundFunc3(typename Base::MutableP2 arg) : Base(arg) {} +}; + +template +struct BoundFunc4 : public BoundFunc { + typedef BoundFunc Base; + typedef I FuncInfo; + explicit BoundFunc4(typename Base::MutableP2 arg) : Base(arg) {} +}; + +template +struct BoundFunc5 : public BoundFunc { + typedef BoundFunc Base; + typedef I FuncInfo; + explicit BoundFunc5(typename Base::MutableP2 arg) : Base(arg) {} +}; + +/* FuncSig ********************************************************************/ + +/* FuncSig1, FuncSig2, FuncSig3: template classes reflecting a function + * *signature*, but without a specific function attached. + * + * These classes contain member functions that can be invoked with a + * specific function to return a Func/BoundFunc class. */ +template +struct FuncSig1 { + template + Func1 > GetFunc() { + return Func1 >(); + } +}; + +template +struct FuncSig2 { + template + Func2 > GetFunc() { + return Func2 >(); + } + + template + BoundFunc2 > GetFunc( + typename remove_constptr::type param2) { + return BoundFunc2 >(param2); + } +}; + +template +struct FuncSig3 { + template + Func3 > GetFunc() { + return Func3 >(); + } + + template + BoundFunc3 > GetFunc( + typename remove_constptr::type param2) { + return BoundFunc3 >(param2); + } +}; + +template +struct FuncSig4 { + template + Func4 > GetFunc() { + return Func4 >(); + } + + template + BoundFunc4 > GetFunc( + typename remove_constptr::type param2) { + return BoundFunc4 >(param2); + } +}; + +template +struct FuncSig5 { + template + Func5 > GetFunc() { + return Func5 >(); + } + + template + BoundFunc5 > GetFunc( + typename remove_constptr::type param2) { + return BoundFunc5 >(param2); + } +}; + +/* Overloaded template function that can construct the appropriate FuncSig* + * class given a function pointer by deducing the template parameters. */ +template +inline FuncSig1 MatchFunc(R (*f)(P1)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return FuncSig1(); +} + +template +inline FuncSig2 MatchFunc(R (*f)(P1, P2)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return FuncSig2(); +} + +template +inline FuncSig3 MatchFunc(R (*f)(P1, P2, P3)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return FuncSig3(); +} + +template +inline FuncSig4 MatchFunc(R (*f)(P1, P2, P3, P4)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return FuncSig4(); +} + +template +inline FuncSig5 MatchFunc(R (*f)(P1, P2, P3, P4, P5)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return FuncSig5(); +} + +/* MethodSig ******************************************************************/ + +/* CallMethod*: a function template that calls a given method. */ +template +R CallMethod0(C *obj) { + return ((*obj).*F)(); +} + +template +R CallMethod1(C *obj, P1 arg1) { + return ((*obj).*F)(arg1); +} + +template +R CallMethod2(C *obj, P1 arg1, P2 arg2) { + return ((*obj).*F)(arg1, arg2); +} + +template +R CallMethod3(C *obj, P1 arg1, P2 arg2, P3 arg3) { + return ((*obj).*F)(arg1, arg2, arg3); +} + +template +R CallMethod4(C *obj, P1 arg1, P2 arg2, P3 arg3, P4 arg4) { + return ((*obj).*F)(arg1, arg2, arg3, arg4); +} + +/* MethodSig: like FuncSig, but for member functions. + * + * GetFunc() returns a normal FuncN object, so after calling GetFunc() no + * more logic is required to special-case methods. */ +template +struct MethodSig0 { + template + Func1, FuncInfo > GetFunc() { + return Func1, FuncInfo >(); + } +}; + +template +struct MethodSig1 { + template + Func2, FuncInfo > GetFunc() { + return Func2, FuncInfo >(); + } + + template + BoundFunc2, FuncInfo > GetFunc( + typename remove_constptr::type param1) { + return BoundFunc2, FuncInfo >( + param1); + } +}; + +template +struct MethodSig2 { + template + Func3, FuncInfo > + GetFunc() { + return Func3, + FuncInfo >(); + } + + template + BoundFunc3, FuncInfo > + GetFunc(typename remove_constptr::type param1) { + return BoundFunc3, + FuncInfo >(param1); + } +}; + +template +struct MethodSig3 { + template + Func4, FuncInfo > + GetFunc() { + return Func4, + FuncInfo >(); + } + + template + BoundFunc4, + FuncInfo > + GetFunc(typename remove_constptr::type param1) { + return BoundFunc4, + FuncInfo >(param1); + } +}; + +template +struct MethodSig4 { + template + Func5, + FuncInfo > + GetFunc() { + return Func5, + FuncInfo >(); + } + + template + BoundFunc5, + FuncInfo > + GetFunc(typename remove_constptr::type param1) { + return BoundFunc5, FuncInfo >( + param1); + } +}; + +template +inline MethodSig0 MatchFunc(R (C::*f)()) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return MethodSig0(); +} + +template +inline MethodSig1 MatchFunc(R (C::*f)(P1)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return MethodSig1(); +} + +template +inline MethodSig2 MatchFunc(R (C::*f)(P1, P2)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return MethodSig2(); +} + +template +inline MethodSig3 MatchFunc(R (C::*f)(P1, P2, P3)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return MethodSig3(); +} + +template +inline MethodSig4 MatchFunc(R (C::*f)(P1, P2, P3, P4)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return MethodSig4(); +} + +/* MaybeWrapReturn ************************************************************/ + +/* Template class that attempts to wrap the return value of the function so it + * matches the expected type. There are two main adjustments it may make: + * + * 1. If the function returns void, make it return the expected type and with + * a value that always indicates success. + * 2. If the function returns bool, make it return the expected type with a + * value that indicates success or failure. + * + * The "expected type" for return is: + * 1. void* for start handlers. If the closure parameter has a different type + * we will cast it to void* for the return in the success case. + * 2. size_t for string buffer handlers. + * 3. bool for everything else. */ + +/* Template parameters are FuncN type and desired return type. */ +template +struct MaybeWrapReturn; + +/* If the return type matches, return the given function unwrapped. */ +template +struct MaybeWrapReturn { + typedef F Func; +}; + +/* Function wrapper that munges the return value from void to (bool)true. */ +template +bool ReturnTrue2(P1 p1, P2 p2) { + F(p1, p2); + return true; +} + +template +bool ReturnTrue3(P1 p1, P2 p2, P3 p3) { + F(p1, p2, p3); + return true; +} + +/* Function wrapper that munges the return value from void to (void*)arg1 */ +template +void *ReturnClosure2(P1 p1, P2 p2) { + F(p1, p2); + return p1; +} + +template +void *ReturnClosure3(P1 p1, P2 p2, P3 p3) { + F(p1, p2, p3); + return p1; +} + +/* Function wrapper that munges the return value from R to void*. */ +template +void *CastReturnToVoidPtr2(P1 p1, P2 p2) { + return F(p1, p2); +} + +template +void *CastReturnToVoidPtr3(P1 p1, P2 p2, P3 p3) { + return F(p1, p2, p3); +} + +/* Function wrapper that munges the return value from bool to void*. */ +template +void *ReturnClosureOrBreak2(P1 p1, P2 p2) { + return F(p1, p2) ? p1 : UPB_BREAK; +} + +template +void *ReturnClosureOrBreak3(P1 p1, P2 p2, P3 p3) { + return F(p1, p2, p3) ? p1 : UPB_BREAK; +} + +/* For the string callback, which takes five params, returns the size param. */ +template +size_t ReturnStringLen(P1 p1, P2 p2, const char *p3, size_t p4, + const BufferHandle *p5) { + F(p1, p2, p3, p4, p5); + return p4; +} + +/* For the string callback, which takes five params, returns the size param or + * zero. */ +template +size_t ReturnNOr0(P1 p1, P2 p2, const char *p3, size_t p4, + const BufferHandle *p5) { + return F(p1, p2, p3, p4, p5) ? p4 : 0; +} + +/* If we have a function returning void but want a function returning bool, wrap + * it in a function that returns true. */ +template +struct MaybeWrapReturn, bool> { + typedef Func2, I> Func; +}; + +template +struct MaybeWrapReturn, bool> { + typedef Func3, I> Func; +}; + +/* If our function returns void but we want one returning void*, wrap it in a + * function that returns the first argument. */ +template +struct MaybeWrapReturn, void *> { + typedef Func2, I> Func; +}; + +template +struct MaybeWrapReturn, void *> { + typedef Func3, I> Func; +}; + +/* If our function returns R* but we want one returning void*, wrap it in a + * function that casts to void*. */ +template +struct MaybeWrapReturn, void *, + typename disable_if_same::Type> { + typedef Func2, I> Func; +}; + +template +struct MaybeWrapReturn, void *, + typename disable_if_same::Type> { + typedef Func3, I> + Func; +}; + +/* If our function returns bool but we want one returning void*, wrap it in a + * function that returns either the first param or UPB_BREAK. */ +template +struct MaybeWrapReturn, void *> { + typedef Func2, I> Func; +}; + +template +struct MaybeWrapReturn, void *> { + typedef Func3, I> + Func; +}; + +/* If our function returns void but we want one returning size_t, wrap it in a + * function that returns the size argument. */ +template +struct MaybeWrapReturn< + Func5, + size_t> { + typedef Func5, I> Func; +}; + +/* If our function returns bool but we want one returning size_t, wrap it in a + * function that returns either 0 or the buf size. */ +template +struct MaybeWrapReturn< + Func5, + size_t> { + typedef Func5, I> Func; +}; + +/* ConvertParams **************************************************************/ + +/* Template class that converts the function parameters if necessary, and + * ignores the HandlerData parameter if appropriate. + * + * Template parameter is the are FuncN function type. */ +template +struct ConvertParams; + +/* Function that discards the handler data parameter. */ +template +R IgnoreHandlerData2(void *p1, const void *hd) { + UPB_UNUSED(hd); + return F(static_cast(p1)); +} + +template +R IgnoreHandlerData3(void *p1, const void *hd, P2Wrapper p2) { + UPB_UNUSED(hd); + return F(static_cast(p1), p2); +} + +template +R IgnoreHandlerData4(void *p1, const void *hd, P2 p2, P3 p3) { + UPB_UNUSED(hd); + return F(static_cast(p1), p2, p3); +} + +template +R IgnoreHandlerData5(void *p1, const void *hd, P2 p2, P3 p3, P4 p4) { + UPB_UNUSED(hd); + return F(static_cast(p1), p2, p3, p4); +} + +template +R IgnoreHandlerDataIgnoreHandle(void *p1, const void *hd, const char *p2, + size_t p3, const BufferHandle *handle) { + UPB_UNUSED(hd); + UPB_UNUSED(handle); + return F(static_cast(p1), p2, p3); +} + +/* Function that casts the handler data parameter. */ +template +R CastHandlerData2(void *c, const void *hd) { + return F(static_cast(c), static_cast(hd)); +} + +template +R CastHandlerData3(void *c, const void *hd, P3Wrapper p3) { + return F(static_cast(c), static_cast(hd), p3); +} + +template +R CastHandlerData5(void *c, const void *hd, P3 p3, P4 p4, P5 p5) { + return F(static_cast(c), static_cast(hd), p3, p4, p5); +} + +template +R CastHandlerDataIgnoreHandle(void *c, const void *hd, const char *p3, + size_t p4, const BufferHandle *handle) { + UPB_UNUSED(handle); + return F(static_cast(c), static_cast(hd), p3, p4); +} + +/* For unbound functions, ignore the handler data. */ +template +struct ConvertParams, T> { + typedef Func2, I> Func; +}; + +template +struct ConvertParams, + R2 (*)(P1_2, P2_2, P3_2)> { + typedef Func3, I> Func; +}; + +/* For StringBuffer only; this ignores both the handler data and the + * BufferHandle. */ +template +struct ConvertParams, T> { + typedef Func5, + I> Func; +}; + +template +struct ConvertParams, T> { + typedef Func5, I> Func; +}; + +/* For bound functions, cast the handler data. */ +template +struct ConvertParams, T> { + typedef Func2, I> + Func; +}; + +template +struct ConvertParams, + R2 (*)(P1_2, P2_2, P3_2)> { + typedef Func3, I> Func; +}; + +/* For StringBuffer only; this ignores the BufferHandle. */ +template +struct ConvertParams, T> { + typedef Func5, + I> Func; +}; + +template +struct ConvertParams, T> { + typedef Func5, I> Func; +}; + +/* utype/ltype are upper/lower-case, ctype is canonical C type, vtype is + * variant C type. */ +#define TYPE_METHODS(utype, ltype, ctype, vtype) \ + template <> struct CanonicalType { \ + typedef ctype Type; \ + }; \ + template <> \ + inline bool Handlers::SetValueHandler( \ + const FieldDef *f, \ + const Handlers::utype ## Handler& handler) { \ + assert(!handler.registered_); \ + handler.AddCleanup(this); \ + handler.registered_ = true; \ + return upb_handlers_set##ltype(this, f, handler.handler_, &handler.attr_); \ + } \ + +TYPE_METHODS(Double, double, double, double) +TYPE_METHODS(Float, float, float, float) +TYPE_METHODS(UInt64, uint64, uint64_t, UPB_UINT64_T) +TYPE_METHODS(UInt32, uint32, uint32_t, UPB_UINT32_T) +TYPE_METHODS(Int64, int64, int64_t, UPB_INT64_T) +TYPE_METHODS(Int32, int32, int32_t, UPB_INT32_T) +TYPE_METHODS(Bool, bool, bool, bool) + +#ifdef UPB_TWO_32BIT_TYPES +TYPE_METHODS(Int32, int32, int32_t, UPB_INT32ALT_T) +TYPE_METHODS(UInt32, uint32, uint32_t, UPB_UINT32ALT_T) +#endif + +#ifdef UPB_TWO_64BIT_TYPES +TYPE_METHODS(Int64, int64, int64_t, UPB_INT64ALT_T) +TYPE_METHODS(UInt64, uint64, uint64_t, UPB_UINT64ALT_T) +#endif +#undef TYPE_METHODS + +template <> struct CanonicalType { + typedef Status* Type; +}; + +/* Type methods that are only one-per-canonical-type and not + * one-per-cvariant. */ + +#define TYPE_METHODS(utype, ctype) \ + inline bool Handlers::Set##utype##Handler(const FieldDef *f, \ + const utype##Handler &h) { \ + return SetValueHandler(f, h); \ + } \ + +TYPE_METHODS(Double, double) +TYPE_METHODS(Float, float) +TYPE_METHODS(UInt64, uint64_t) +TYPE_METHODS(UInt32, uint32_t) +TYPE_METHODS(Int64, int64_t) +TYPE_METHODS(Int32, int32_t) +TYPE_METHODS(Bool, bool) +#undef TYPE_METHODS + +template struct ReturnOf; + +template +struct ReturnOf { + typedef R Return; +}; + +template +struct ReturnOf { + typedef R Return; +}; + +template +struct ReturnOf { + typedef R Return; +}; + +template +struct ReturnOf { + typedef R Return; +}; + +template const void *UniquePtrForType() { + static const char ch = 0; + return &ch; +} + +template +template +inline Handler::Handler(F func) + : registered_(false), + cleanup_data_(func.GetData()), + cleanup_func_(func.GetCleanup()) { + upb_handlerattr_sethandlerdata(&attr_, func.GetData()); + typedef typename ReturnOf::Return Return; + typedef typename ConvertParams::Func ConvertedParamsFunc; + typedef typename MaybeWrapReturn::Func + ReturnWrappedFunc; + handler_ = ReturnWrappedFunc().Call; + + /* Set attributes based on what templates can statically tell us about the + * user's function. */ + + /* If the original function returns void, then we know that we wrapped it to + * always return ok. */ + bool always_ok = is_same::value; + attr_.SetAlwaysOk(always_ok); + + /* Closure parameter and return type. */ + attr_.SetClosureType(UniquePtrForType()); + + /* We use the closure type (from the first parameter) if the return type is + * void or bool, since these are the two cases we wrap to return the closure's + * type anyway. + * + * This is all nonsense for non START* handlers, but it doesn't matter because + * in that case the value will be ignored. */ + typedef typename FirstUnlessVoidOrBool::value + EffectiveReturn; + attr_.SetReturnClosureType(UniquePtrForType()); +} + +template +inline Handler::~Handler() { + assert(registered_); +} + +inline HandlerAttributes::HandlerAttributes() { upb_handlerattr_init(this); } +inline HandlerAttributes::~HandlerAttributes() { upb_handlerattr_uninit(this); } +inline bool HandlerAttributes::SetHandlerData(const void *hd) { + return upb_handlerattr_sethandlerdata(this, hd); +} +inline const void* HandlerAttributes::handler_data() const { + return upb_handlerattr_handlerdata(this); +} +inline bool HandlerAttributes::SetClosureType(const void *type) { + return upb_handlerattr_setclosuretype(this, type); +} +inline const void* HandlerAttributes::closure_type() const { + return upb_handlerattr_closuretype(this); +} +inline bool HandlerAttributes::SetReturnClosureType(const void *type) { + return upb_handlerattr_setreturnclosuretype(this, type); +} +inline const void* HandlerAttributes::return_closure_type() const { + return upb_handlerattr_returnclosuretype(this); +} +inline bool HandlerAttributes::SetAlwaysOk(bool always_ok) { + return upb_handlerattr_setalwaysok(this, always_ok); +} +inline bool HandlerAttributes::always_ok() const { + return upb_handlerattr_alwaysok(this); +} + +inline BufferHandle::BufferHandle() { upb_bufhandle_init(this); } +inline BufferHandle::~BufferHandle() { upb_bufhandle_uninit(this); } +inline const char* BufferHandle::buffer() const { + return upb_bufhandle_buf(this); +} +inline size_t BufferHandle::object_offset() const { + return upb_bufhandle_objofs(this); +} +inline void BufferHandle::SetBuffer(const char* buf, size_t ofs) { + upb_bufhandle_setbuf(this, buf, ofs); +} +template +void BufferHandle::SetAttachedObject(const T* obj) { + upb_bufhandle_setobj(this, obj, UniquePtrForType()); +} +template +const T* BufferHandle::GetAttachedObject() const { + return upb_bufhandle_objtype(this) == UniquePtrForType() + ? static_cast(upb_bufhandle_obj(this)) + : NULL; +} + +inline reffed_ptr Handlers::New(const MessageDef *m) { + upb_handlers *h = upb_handlers_new(m, &h); + return reffed_ptr(h, &h); +} +inline reffed_ptr Handlers::NewFrozen( + const MessageDef *m, upb_handlers_callback *callback, + const void *closure) { + const upb_handlers *h = upb_handlers_newfrozen(m, &h, callback, closure); + return reffed_ptr(h, &h); +} +inline const Status* Handlers::status() { + return upb_handlers_status(this); +} +inline void Handlers::ClearError() { + return upb_handlers_clearerr(this); +} +inline bool Handlers::Freeze(Status *s) { + upb::Handlers* h = this; + return upb_handlers_freeze(&h, 1, s); +} +inline bool Handlers::Freeze(Handlers *const *handlers, int n, Status *s) { + return upb_handlers_freeze(handlers, n, s); +} +inline bool Handlers::Freeze(const std::vector& h, Status* status) { + return upb_handlers_freeze((Handlers* const*)&h[0], h.size(), status); +} +inline const MessageDef *Handlers::message_def() const { + return upb_handlers_msgdef(this); +} +inline bool Handlers::AddCleanup(void *p, upb_handlerfree *func) { + return upb_handlers_addcleanup(this, p, func); +} +inline bool Handlers::SetStartMessageHandler( + const Handlers::StartMessageHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setstartmsg(this, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetEndMessageHandler( + const Handlers::EndMessageHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setendmsg(this, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetStartStringHandler(const FieldDef *f, + const StartStringHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setstartstr(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetEndStringHandler(const FieldDef *f, + const EndFieldHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setendstr(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetStringHandler(const FieldDef *f, + const StringHandler& handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setstring(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetStartSequenceHandler( + const FieldDef *f, const StartFieldHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setstartseq(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetStartSubMessageHandler( + const FieldDef *f, const StartFieldHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setstartsubmsg(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetEndSubMessageHandler(const FieldDef *f, + const EndFieldHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setendsubmsg(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetEndSequenceHandler(const FieldDef *f, + const EndFieldHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setendseq(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetSubHandlers(const FieldDef *f, const Handlers *sub) { + return upb_handlers_setsubhandlers(this, f, sub); +} +inline const Handlers *Handlers::GetSubHandlers(const FieldDef *f) const { + return upb_handlers_getsubhandlers(this, f); +} +inline const Handlers *Handlers::GetSubHandlers(Handlers::Selector sel) const { + return upb_handlers_getsubhandlers_sel(this, sel); +} +inline bool Handlers::GetSelector(const FieldDef *f, Handlers::Type type, + Handlers::Selector *s) { + return upb_handlers_getselector(f, type, s); +} +inline Handlers::Selector Handlers::GetEndSelector(Handlers::Selector start) { + return upb_handlers_getendselector(start); +} +inline Handlers::GenericFunction *Handlers::GetHandler( + Handlers::Selector selector) { + return upb_handlers_gethandler(this, selector); +} +inline const void *Handlers::GetHandlerData(Handlers::Selector selector) { + return upb_handlers_gethandlerdata(this, selector); +} + +inline BytesHandler::BytesHandler() { + upb_byteshandler_init(this); +} + +inline BytesHandler::~BytesHandler() {} + +} /* namespace upb */ + +#endif /* __cplusplus */ + + +#undef UPB_TWO_32BIT_TYPES +#undef UPB_TWO_64BIT_TYPES +#undef UPB_INT32_T +#undef UPB_UINT32_T +#undef UPB_INT32ALT_T +#undef UPB_UINT32ALT_T +#undef UPB_INT64_T +#undef UPB_UINT64_T +#undef UPB_INT64ALT_T +#undef UPB_UINT64ALT_T + +#endif /* UPB_HANDLERS_INL_H_ */ + +#endif /* UPB_HANDLERS_H */ +/* +** upb::Environment (upb_env) +** +** A upb::Environment provides a means for injecting malloc and an +** error-reporting callback into encoders/decoders. This allows them to be +** independent of nearly all assumptions about their actual environment. +** +** It is also a container for allocating the encoders/decoders themselves that +** insulates clients from knowing their actual size. This provides ABI +** compatibility even if the size of the objects change. And this allows the +** structure definitions to be in the .c files instead of the .h files, making +** the .h files smaller and more readable. +*/ + + +#ifndef UPB_ENV_H_ +#define UPB_ENV_H_ + +#ifdef __cplusplus +namespace upb { +class Environment; +class SeededAllocator; +} +#endif + +UPB_DECLARE_TYPE(upb::Environment, upb_env) +UPB_DECLARE_TYPE(upb::SeededAllocator, upb_seededalloc) + +typedef void *upb_alloc_func(void *ud, void *ptr, size_t oldsize, size_t size); +typedef void upb_cleanup_func(void *ud); +typedef bool upb_error_func(void *ud, const upb_status *status); + +#ifdef __cplusplus + +/* An environment is *not* thread-safe. */ +class upb::Environment { + public: + Environment(); + ~Environment(); + + /* Set a custom memory allocation function for the environment. May ONLY + * be called before any calls to Malloc()/Realloc()/AddCleanup() below. + * If this is not called, the system realloc() function will be used. + * The given user pointer "ud" will be passed to the allocation function. + * + * The allocation function will not receive corresponding "free" calls. it + * must ensure that the memory is valid for the lifetime of the Environment, + * but it may be reclaimed any time thereafter. The likely usage is that + * "ud" points to a stateful allocator, and that the allocator frees all + * memory, arena-style, when it is destroyed. In this case the allocator must + * outlive the Environment. Another possibility is that the allocation + * function returns GC-able memory that is guaranteed to be GC-rooted for the + * life of the Environment. */ + void SetAllocationFunction(upb_alloc_func* alloc, void* ud); + + template + void SetAllocator(T* allocator) { + SetAllocationFunction(allocator->GetAllocationFunction(), allocator); + } + + /* Set a custom error reporting function. */ + void SetErrorFunction(upb_error_func* func, void* ud); + + /* Set the error reporting function to simply copy the status to the given + * status and abort. */ + void ReportErrorsTo(Status* status); + + /* Returns true if all allocations and AddCleanup() calls have succeeded, + * and no errors were reported with ReportError() (except ones that recovered + * successfully). */ + bool ok() const; + + /* Functions for use by encoders/decoders. **********************************/ + + /* Reports an error to this environment's callback, returning true if + * the caller should try to recover. */ + bool ReportError(const Status* status); + + /* Allocate memory. Uses the environment's allocation function. + * + * There is no need to free(). All memory will be freed automatically, but is + * guaranteed to outlive the Environment. */ + void* Malloc(size_t size); + + /* Reallocate memory. Preserves "oldsize" bytes from the existing buffer + * Requires: oldsize <= existing_size. + * + * TODO(haberman): should we also enforce that oldsize <= size? */ + void* Realloc(void* ptr, size_t oldsize, size_t size); + + /* Add a cleanup function to run when the environment is destroyed. + * Returns false on out-of-memory. + * + * The first call to AddCleanup() after SetAllocationFunction() is guaranteed + * to return true -- this makes it possible to robustly set a cleanup handler + * for a custom allocation function. */ + bool AddCleanup(upb_cleanup_func* func, void* ud); + + /* Total number of bytes that have been allocated. It is undefined what + * Realloc() does to this counter. */ + size_t BytesAllocated() const; + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(Environment) + +#else +struct upb_env { +#endif /* __cplusplus */ + + bool ok_; + size_t bytes_allocated; + + /* Alloc function. */ + upb_alloc_func *alloc; + void *alloc_ud; + + /* Error-reporting function. */ + upb_error_func *err; + void *err_ud; + + /* Userdata for default alloc func. */ + void *default_alloc_ud; + + /* Cleanup entries. Pointer to a cleanup_ent, defined in env.c */ + void *cleanup_head; + + /* For future expansion, since the size of this struct is exposed to users. */ + void *future1; + void *future2; +}; + +UPB_BEGIN_EXTERN_C + +void upb_env_init(upb_env *e); +void upb_env_uninit(upb_env *e); +void upb_env_setallocfunc(upb_env *e, upb_alloc_func *func, void *ud); +void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud); +void upb_env_reporterrorsto(upb_env *e, upb_status *status); +bool upb_env_ok(const upb_env *e); +bool upb_env_reporterror(upb_env *e, const upb_status *status); +void *upb_env_malloc(upb_env *e, size_t size); +void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size); +bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud); +size_t upb_env_bytesallocated(const upb_env *e); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +/* An allocator that allocates from an initial memory region (likely the stack) + * before falling back to another allocator. */ +class upb::SeededAllocator { + public: + SeededAllocator(void *mem, size_t len); + ~SeededAllocator(); + + /* Set a custom fallback memory allocation function for the allocator, to use + * once the initial region runs out. + * + * May ONLY be called before GetAllocationFunction(). If this is not + * called, the system realloc() will be the fallback allocator. */ + void SetFallbackAllocator(upb_alloc_func *alloc, void *ud); + + /* Gets the allocation function for this allocator. */ + upb_alloc_func* GetAllocationFunction(); + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(SeededAllocator) + +#else +struct upb_seededalloc { +#endif /* __cplusplus */ + + /* Fallback alloc function. */ + upb_alloc_func *alloc; + upb_cleanup_func *alloc_cleanup; + void *alloc_ud; + bool need_cleanup; + bool returned_allocfunc; + + /* Userdata for default alloc func. */ + void *default_alloc_ud; + + /* Pointers for the initial memory region. */ + char *mem_base; + char *mem_ptr; + char *mem_limit; + + /* For future expansion, since the size of this struct is exposed to users. */ + void *future1; + void *future2; +}; + +UPB_BEGIN_EXTERN_C + +void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len); +void upb_seededalloc_uninit(upb_seededalloc *a); +void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, upb_alloc_func *func, + void *ud); +upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +namespace upb { + +inline Environment::Environment() { + upb_env_init(this); +} +inline Environment::~Environment() { + upb_env_uninit(this); +} +inline void Environment::SetAllocationFunction(upb_alloc_func *alloc, + void *ud) { + upb_env_setallocfunc(this, alloc, ud); +} +inline void Environment::SetErrorFunction(upb_error_func *func, void *ud) { + upb_env_seterrorfunc(this, func, ud); +} +inline void Environment::ReportErrorsTo(Status* status) { + upb_env_reporterrorsto(this, status); +} +inline bool Environment::ok() const { + return upb_env_ok(this); +} +inline bool Environment::ReportError(const Status* status) { + return upb_env_reporterror(this, status); +} +inline void *Environment::Malloc(size_t size) { + return upb_env_malloc(this, size); +} +inline void *Environment::Realloc(void *ptr, size_t oldsize, size_t size) { + return upb_env_realloc(this, ptr, oldsize, size); +} +inline bool Environment::AddCleanup(upb_cleanup_func *func, void *ud) { + return upb_env_addcleanup(this, func, ud); +} +inline size_t Environment::BytesAllocated() const { + return upb_env_bytesallocated(this); +} + +inline SeededAllocator::SeededAllocator(void *mem, size_t len) { + upb_seededalloc_init(this, mem, len); +} +inline SeededAllocator::~SeededAllocator() { + upb_seededalloc_uninit(this); +} +inline void SeededAllocator::SetFallbackAllocator(upb_alloc_func *alloc, + void *ud) { + upb_seededalloc_setfallbackalloc(this, alloc, ud); +} +inline upb_alloc_func *SeededAllocator::GetAllocationFunction() { + return upb_seededalloc_getallocfunc(this); +} + +} /* namespace upb */ + +#endif /* __cplusplus */ + +#endif /* UPB_ENV_H_ */ +/* +** upb::Sink (upb_sink) +** upb::BytesSink (upb_bytessink) +** +** A upb_sink is an object that binds a upb_handlers object to some runtime +** state. It is the object that can actually receive data via the upb_handlers +** interface. +** +** Unlike upb_def and upb_handlers, upb_sink is never frozen, immutable, or +** thread-safe. You can create as many of them as you want, but each one may +** only be used in a single thread at a time. +** +** If we compare with class-based OOP, a you can think of a upb_def as an +** abstract base class, a upb_handlers as a concrete derived class, and a +** upb_sink as an object (class instance). +*/ + +#ifndef UPB_SINK_H +#define UPB_SINK_H + + +#ifdef __cplusplus +namespace upb { +class BufferSource; +class BytesSink; +class Sink; +} +#endif + +UPB_DECLARE_TYPE(upb::BufferSource, upb_bufsrc) +UPB_DECLARE_TYPE(upb::BytesSink, upb_bytessink) +UPB_DECLARE_TYPE(upb::Sink, upb_sink) + +#ifdef __cplusplus + +/* A upb::Sink is an object that binds a upb::Handlers object to some runtime + * state. It represents an endpoint to which data can be sent. + * + * TODO(haberman): right now all of these functions take selectors. Should they + * take selectorbase instead? + * + * ie. instead of calling: + * sink->StartString(FOO_FIELD_START_STRING, ...) + * a selector base would let you say: + * sink->StartString(FOO_FIELD, ...) + * + * This would make call sites a little nicer and require emitting fewer selector + * definitions in .h files. + * + * But the current scheme has the benefit that you can retrieve a function + * pointer for any handler with handlers->GetHandler(selector), without having + * to have a separate GetHandler() function for each handler type. The JIT + * compiler uses this. To accommodate we'd have to expose a separate + * GetHandler() for every handler type. + * + * Also to ponder: selectors right now are independent of a specific Handlers + * instance. In other words, they allocate a number to every possible handler + * that *could* be registered, without knowing anything about what handlers + * *are* registered. That means that using selectors as table offsets prohibits + * us from compacting the handler table at Freeze() time. If the table is very + * sparse, this could be wasteful. + * + * Having another selector-like thing that is specific to a Handlers instance + * would allow this compacting, but then it would be impossible to write code + * ahead-of-time that can be bound to any Handlers instance at runtime. For + * example, a .proto file parser written as straight C will not know what + * Handlers it will be bound to, so when it calls sink->StartString() what + * selector will it pass? It needs a selector like we have today, that is + * independent of any particular upb::Handlers. + * + * Is there a way then to allow Handlers table compaction? */ +class upb::Sink { + public: + /* Constructor with no initialization; must be Reset() before use. */ + Sink() {} + + /* Constructs a new sink for the given frozen handlers and closure. + * + * TODO: once the Handlers know the expected closure type, verify that T + * matches it. */ + template Sink(const Handlers* handlers, T* closure); + + /* Resets the value of the sink. */ + template void Reset(const Handlers* handlers, T* closure); + + /* Returns the top-level object that is bound to this sink. + * + * TODO: once the Handlers know the expected closure type, verify that T + * matches it. */ + template T* GetObject() const; + + /* Functions for pushing data into the sink. + * + * These return false if processing should stop (either due to error or just + * to suspend). + * + * These may not be called from within one of the same sink's handlers (in + * other words, handlers are not re-entrant). */ + + /* Should be called at the start and end of every message; both the top-level + * message and submessages. This means that submessages should use the + * following sequence: + * sink->StartSubMessage(startsubmsg_selector); + * sink->StartMessage(); + * // ... + * sink->EndMessage(&status); + * sink->EndSubMessage(endsubmsg_selector); */ + bool StartMessage(); + bool EndMessage(Status* status); + + /* Putting of individual values. These work for both repeated and + * non-repeated fields, but for repeated fields you must wrap them in + * calls to StartSequence()/EndSequence(). */ + bool PutInt32(Handlers::Selector s, int32_t val); + bool PutInt64(Handlers::Selector s, int64_t val); + bool PutUInt32(Handlers::Selector s, uint32_t val); + bool PutUInt64(Handlers::Selector s, uint64_t val); + bool PutFloat(Handlers::Selector s, float val); + bool PutDouble(Handlers::Selector s, double val); + bool PutBool(Handlers::Selector s, bool val); + + /* Putting of string/bytes values. Each string can consist of zero or more + * non-contiguous buffers of data. + * + * For StartString(), the function will write a sink for the string to "sub." + * The sub-sink must be used for any/all PutStringBuffer() calls. */ + bool StartString(Handlers::Selector s, size_t size_hint, Sink* sub); + size_t PutStringBuffer(Handlers::Selector s, const char *buf, size_t len, + const BufferHandle *handle); + bool EndString(Handlers::Selector s); + + /* For submessage fields. + * + * For StartSubMessage(), the function will write a sink for the string to + * "sub." The sub-sink must be used for any/all handlers called within the + * submessage. */ + bool StartSubMessage(Handlers::Selector s, Sink* sub); + bool EndSubMessage(Handlers::Selector s); + + /* For repeated fields of any type, the sequence of values must be wrapped in + * these calls. + * + * For StartSequence(), the function will write a sink for the string to + * "sub." The sub-sink must be used for any/all handlers called within the + * sequence. */ + bool StartSequence(Handlers::Selector s, Sink* sub); + bool EndSequence(Handlers::Selector s); + + /* Copy and assign specifically allowed. + * We don't even bother making these members private because so many + * functions need them and this is mainly just a dumb data container anyway. + */ +#else +struct upb_sink { +#endif + const upb_handlers *handlers; + void *closure; +}; + +#ifdef __cplusplus +class upb::BytesSink { + public: + BytesSink() {} + + /* Constructs a new sink for the given frozen handlers and closure. + * + * TODO(haberman): once the Handlers know the expected closure type, verify + * that T matches it. */ + template BytesSink(const BytesHandler* handler, T* closure); + + /* Resets the value of the sink. */ + template void Reset(const BytesHandler* handler, T* closure); + + bool Start(size_t size_hint, void **subc); + size_t PutBuffer(void *subc, const char *buf, size_t len, + const BufferHandle *handle); + bool End(); +#else +struct upb_bytessink { +#endif + const upb_byteshandler *handler; + void *closure; +}; + +#ifdef __cplusplus + +/* A class for pushing a flat buffer of data to a BytesSink. + * You can construct an instance of this to get a resumable source, + * or just call the static PutBuffer() to do a non-resumable push all in one + * go. */ +class upb::BufferSource { + public: + BufferSource(); + BufferSource(const char* buf, size_t len, BytesSink* sink); + + /* Returns true if the entire buffer was pushed successfully. Otherwise the + * next call to PutNext() will resume where the previous one left off. + * TODO(haberman): implement this. */ + bool PutNext(); + + /* A static version; with this version is it not possible to resume in the + * case of failure or a partially-consumed buffer. */ + static bool PutBuffer(const char* buf, size_t len, BytesSink* sink); + + template static bool PutBuffer(const T& str, BytesSink* sink) { + return PutBuffer(str.c_str(), str.size(), sink); + } +#else +struct upb_bufsrc { + char dummy; +#endif +}; + +UPB_BEGIN_EXTERN_C + +/* Inline definitions. */ + +UPB_INLINE void upb_bytessink_reset(upb_bytessink *s, const upb_byteshandler *h, + void *closure) { + s->handler = h; + s->closure = closure; +} + +UPB_INLINE bool upb_bytessink_start(upb_bytessink *s, size_t size_hint, + void **subc) { + typedef upb_startstr_handlerfunc func; + func *start; + *subc = s->closure; + if (!s->handler) return true; + start = (func *)s->handler->table[UPB_STARTSTR_SELECTOR].func; + + if (!start) return true; + *subc = start(s->closure, upb_handlerattr_handlerdata( + &s->handler->table[UPB_STARTSTR_SELECTOR].attr), + size_hint); + return *subc != NULL; +} + +UPB_INLINE size_t upb_bytessink_putbuf(upb_bytessink *s, void *subc, + const char *buf, size_t size, + const upb_bufhandle* handle) { + typedef upb_string_handlerfunc func; + func *putbuf; + if (!s->handler) return true; + putbuf = (func *)s->handler->table[UPB_STRING_SELECTOR].func; + + if (!putbuf) return true; + return putbuf(subc, upb_handlerattr_handlerdata( + &s->handler->table[UPB_STRING_SELECTOR].attr), + buf, size, handle); +} + +UPB_INLINE bool upb_bytessink_end(upb_bytessink *s) { + typedef upb_endfield_handlerfunc func; + func *end; + if (!s->handler) return true; + end = (func *)s->handler->table[UPB_ENDSTR_SELECTOR].func; + + if (!end) return true; + return end(s->closure, + upb_handlerattr_handlerdata( + &s->handler->table[UPB_ENDSTR_SELECTOR].attr)); +} + +UPB_INLINE bool upb_bufsrc_putbuf(const char *buf, size_t len, + upb_bytessink *sink) { + void *subc; + bool ret; + upb_bufhandle handle; + upb_bufhandle_init(&handle); + upb_bufhandle_setbuf(&handle, buf, 0); + ret = upb_bytessink_start(sink, len, &subc); + if (ret && len != 0) { + ret = (upb_bytessink_putbuf(sink, subc, buf, len, &handle) >= len); + } + if (ret) { + ret = upb_bytessink_end(sink); + } + upb_bufhandle_uninit(&handle); + return ret; +} + +#define PUTVAL(type, ctype) \ + UPB_INLINE bool upb_sink_put##type(upb_sink *s, upb_selector_t sel, \ + ctype val) { \ + typedef upb_##type##_handlerfunc functype; \ + functype *func; \ + const void *hd; \ + if (!s->handlers) return true; \ + func = (functype *)upb_handlers_gethandler(s->handlers, sel); \ + if (!func) return true; \ + hd = upb_handlers_gethandlerdata(s->handlers, sel); \ + return func(s->closure, hd, val); \ + } + +PUTVAL(int32, int32_t) +PUTVAL(int64, int64_t) +PUTVAL(uint32, uint32_t) +PUTVAL(uint64, uint64_t) +PUTVAL(float, float) +PUTVAL(double, double) +PUTVAL(bool, bool) +#undef PUTVAL + +UPB_INLINE void upb_sink_reset(upb_sink *s, const upb_handlers *h, void *c) { + s->handlers = h; + s->closure = c; +} + +UPB_INLINE size_t upb_sink_putstring(upb_sink *s, upb_selector_t sel, + const char *buf, size_t n, + const upb_bufhandle *handle) { + typedef upb_string_handlerfunc func; + func *handler; + const void *hd; + if (!s->handlers) return n; + handler = (func *)upb_handlers_gethandler(s->handlers, sel); + + if (!handler) return n; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + return handler(s->closure, hd, buf, n, handle); +} + +UPB_INLINE bool upb_sink_startmsg(upb_sink *s) { + typedef upb_startmsg_handlerfunc func; + func *startmsg; + const void *hd; + if (!s->handlers) return true; + startmsg = (func*)upb_handlers_gethandler(s->handlers, UPB_STARTMSG_SELECTOR); + + if (!startmsg) return true; + hd = upb_handlers_gethandlerdata(s->handlers, UPB_STARTMSG_SELECTOR); + return startmsg(s->closure, hd); +} + +UPB_INLINE bool upb_sink_endmsg(upb_sink *s, upb_status *status) { + typedef upb_endmsg_handlerfunc func; + func *endmsg; + const void *hd; + if (!s->handlers) return true; + endmsg = (func *)upb_handlers_gethandler(s->handlers, UPB_ENDMSG_SELECTOR); + + if (!endmsg) return true; + hd = upb_handlers_gethandlerdata(s->handlers, UPB_ENDMSG_SELECTOR); + return endmsg(s->closure, hd, status); +} + +UPB_INLINE bool upb_sink_startseq(upb_sink *s, upb_selector_t sel, + upb_sink *sub) { + typedef upb_startfield_handlerfunc func; + func *startseq; + const void *hd; + sub->closure = s->closure; + sub->handlers = s->handlers; + if (!s->handlers) return true; + startseq = (func*)upb_handlers_gethandler(s->handlers, sel); + + if (!startseq) return true; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + sub->closure = startseq(s->closure, hd); + return sub->closure ? true : false; +} + +UPB_INLINE bool upb_sink_endseq(upb_sink *s, upb_selector_t sel) { + typedef upb_endfield_handlerfunc func; + func *endseq; + const void *hd; + if (!s->handlers) return true; + endseq = (func*)upb_handlers_gethandler(s->handlers, sel); + + if (!endseq) return true; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + return endseq(s->closure, hd); +} + +UPB_INLINE bool upb_sink_startstr(upb_sink *s, upb_selector_t sel, + size_t size_hint, upb_sink *sub) { + typedef upb_startstr_handlerfunc func; + func *startstr; + const void *hd; + sub->closure = s->closure; + sub->handlers = s->handlers; + if (!s->handlers) return true; + startstr = (func*)upb_handlers_gethandler(s->handlers, sel); + + if (!startstr) return true; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + sub->closure = startstr(s->closure, hd, size_hint); + return sub->closure ? true : false; +} + +UPB_INLINE bool upb_sink_endstr(upb_sink *s, upb_selector_t sel) { + typedef upb_endfield_handlerfunc func; + func *endstr; + const void *hd; + if (!s->handlers) return true; + endstr = (func*)upb_handlers_gethandler(s->handlers, sel); + + if (!endstr) return true; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + return endstr(s->closure, hd); +} + +UPB_INLINE bool upb_sink_startsubmsg(upb_sink *s, upb_selector_t sel, + upb_sink *sub) { + typedef upb_startfield_handlerfunc func; + func *startsubmsg; + const void *hd; + sub->closure = s->closure; + if (!s->handlers) { + sub->handlers = NULL; + return true; + } + sub->handlers = upb_handlers_getsubhandlers_sel(s->handlers, sel); + startsubmsg = (func*)upb_handlers_gethandler(s->handlers, sel); + + if (!startsubmsg) return true; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + sub->closure = startsubmsg(s->closure, hd); + return sub->closure ? true : false; +} + +UPB_INLINE bool upb_sink_endsubmsg(upb_sink *s, upb_selector_t sel) { + typedef upb_endfield_handlerfunc func; + func *endsubmsg; + const void *hd; + if (!s->handlers) return true; + endsubmsg = (func*)upb_handlers_gethandler(s->handlers, sel); + + if (!endsubmsg) return s->closure; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + return endsubmsg(s->closure, hd); +} + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +namespace upb { + +template Sink::Sink(const Handlers* handlers, T* closure) { + upb_sink_reset(this, handlers, closure); +} +template +inline void Sink::Reset(const Handlers* handlers, T* closure) { + upb_sink_reset(this, handlers, closure); +} +inline bool Sink::StartMessage() { + return upb_sink_startmsg(this); +} +inline bool Sink::EndMessage(Status* status) { + return upb_sink_endmsg(this, status); +} +inline bool Sink::PutInt32(Handlers::Selector sel, int32_t val) { + return upb_sink_putint32(this, sel, val); +} +inline bool Sink::PutInt64(Handlers::Selector sel, int64_t val) { + return upb_sink_putint64(this, sel, val); +} +inline bool Sink::PutUInt32(Handlers::Selector sel, uint32_t val) { + return upb_sink_putuint32(this, sel, val); +} +inline bool Sink::PutUInt64(Handlers::Selector sel, uint64_t val) { + return upb_sink_putuint64(this, sel, val); +} +inline bool Sink::PutFloat(Handlers::Selector sel, float val) { + return upb_sink_putfloat(this, sel, val); +} +inline bool Sink::PutDouble(Handlers::Selector sel, double val) { + return upb_sink_putdouble(this, sel, val); +} +inline bool Sink::PutBool(Handlers::Selector sel, bool val) { + return upb_sink_putbool(this, sel, val); +} +inline bool Sink::StartString(Handlers::Selector sel, size_t size_hint, + Sink *sub) { + return upb_sink_startstr(this, sel, size_hint, sub); +} +inline size_t Sink::PutStringBuffer(Handlers::Selector sel, const char *buf, + size_t len, const BufferHandle* handle) { + return upb_sink_putstring(this, sel, buf, len, handle); +} +inline bool Sink::EndString(Handlers::Selector sel) { + return upb_sink_endstr(this, sel); +} +inline bool Sink::StartSubMessage(Handlers::Selector sel, Sink* sub) { + return upb_sink_startsubmsg(this, sel, sub); +} +inline bool Sink::EndSubMessage(Handlers::Selector sel) { + return upb_sink_endsubmsg(this, sel); +} +inline bool Sink::StartSequence(Handlers::Selector sel, Sink* sub) { + return upb_sink_startseq(this, sel, sub); +} +inline bool Sink::EndSequence(Handlers::Selector sel) { + return upb_sink_endseq(this, sel); +} + +template +BytesSink::BytesSink(const BytesHandler* handler, T* closure) { + Reset(handler, closure); +} + +template +void BytesSink::Reset(const BytesHandler *handler, T *closure) { + upb_bytessink_reset(this, handler, closure); +} +inline bool BytesSink::Start(size_t size_hint, void **subc) { + return upb_bytessink_start(this, size_hint, subc); +} +inline size_t BytesSink::PutBuffer(void *subc, const char *buf, size_t len, + const BufferHandle *handle) { + return upb_bytessink_putbuf(this, subc, buf, len, handle); +} +inline bool BytesSink::End() { + return upb_bytessink_end(this); +} + +inline bool BufferSource::PutBuffer(const char *buf, size_t len, + BytesSink *sink) { + return upb_bufsrc_putbuf(buf, len, sink); +} + +} /* namespace upb */ +#endif + +#endif +/* +** For handlers that do very tiny, very simple operations, the function call +** overhead of calling a handler can be significant. This file allows the +** user to define handlers that do something very simple like store the value +** to memory and/or set a hasbit. JIT compilers can then special-case these +** handlers and emit specialized code for them instead of actually calling the +** handler. +** +** The functionality is very simple/limited right now but may expand to be able +** to call another function. +*/ + +#ifndef UPB_SHIM_H +#define UPB_SHIM_H + + +typedef struct { + size_t offset; + int32_t hasbit; +} upb_shim_data; + +#ifdef __cplusplus + +namespace upb { + +struct Shim { + typedef upb_shim_data Data; + + /* Sets a handler for the given field that writes the value to the given + * offset and, if hasbit >= 0, sets a bit at the given bit offset. Returns + * true if the handler was set successfully. */ + static bool Set(Handlers *h, const FieldDef *f, size_t ofs, int32_t hasbit); + + /* If this handler is a shim, returns the corresponding upb::Shim::Data and + * stores the type in "type". Otherwise returns NULL. */ + static const Data* GetData(const Handlers* h, Handlers::Selector s, + FieldDef::Type* type); +}; + +} /* namespace upb */ + +#endif + +UPB_BEGIN_EXTERN_C + +/* C API. */ +bool upb_shim_set(upb_handlers *h, const upb_fielddef *f, size_t offset, + int32_t hasbit); +const upb_shim_data *upb_shim_getdata(const upb_handlers *h, upb_selector_t s, + upb_fieldtype_t *type); + +UPB_END_EXTERN_C + +#ifdef __cplusplus +/* C++ Wrappers. */ +namespace upb { +inline bool Shim::Set(Handlers* h, const FieldDef* f, size_t ofs, + int32_t hasbit) { + return upb_shim_set(h, f, ofs, hasbit); +} +inline const Shim::Data* Shim::GetData(const Handlers* h, Handlers::Selector s, + FieldDef::Type* type) { + return upb_shim_getdata(h, s, type); +} +} /* namespace upb */ +#endif + +#endif /* UPB_SHIM_H */ +/* +** upb::SymbolTable (upb_symtab) +** +** A symtab (symbol table) stores a name->def map of upb_defs. Clients could +** always create such tables themselves, but upb_symtab has logic for resolving +** symbolic references, and in particular, for keeping a whole set of consistent +** defs when replacing some subset of those defs. This logic is nontrivial. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ + +#ifndef UPB_SYMTAB_H_ +#define UPB_SYMTAB_H_ + + +#ifdef __cplusplus +#include +namespace upb { class SymbolTable; } +#endif + +UPB_DECLARE_DERIVED_TYPE(upb::SymbolTable, upb::RefCounted, + upb_symtab, upb_refcounted) + +typedef struct { + UPB_PRIVATE_FOR_CPP + upb_strtable_iter iter; + upb_deftype_t type; +} upb_symtab_iter; + +#ifdef __cplusplus + +/* Non-const methods in upb::SymbolTable are NOT thread-safe. */ +class upb::SymbolTable { + public: + /* Returns a new symbol table with a single ref owned by "owner." + * Returns NULL if memory allocation failed. */ + static reffed_ptr New(); + + /* Include RefCounted base methods. */ + UPB_REFCOUNTED_CPPMETHODS + + /* For all lookup functions, the returned pointer is not owned by the + * caller; it may be invalidated by any non-const call or unref of the + * SymbolTable! To protect against this, take a ref if desired. */ + + /* Freezes the symbol table: prevents further modification of it. + * After the Freeze() operation is successful, the SymbolTable must only be + * accessed via a const pointer. + * + * Unlike with upb::MessageDef/upb::EnumDef/etc, freezing a SymbolTable is not + * a necessary step in using a SymbolTable. If you have no need for it to be + * immutable, there is no need to freeze it ever. However sometimes it is + * useful, and SymbolTables that are statically compiled into the binary are + * always frozen by nature. */ + void Freeze(); + + /* Resolves the given symbol using the rules described in descriptor.proto, + * namely: + * + * If the name starts with a '.', it is fully-qualified. Otherwise, + * C++-like scoping rules are used to find the type (i.e. first the nested + * types within this message are searched, then within the parent, on up + * to the root namespace). + * + * If not found, returns NULL. */ + const Def* Resolve(const char* base, const char* sym) const; + + /* Finds an entry in the symbol table with this exact name. If not found, + * returns NULL. */ + const Def* Lookup(const char *sym) const; + const MessageDef* LookupMessage(const char *sym) const; + const EnumDef* LookupEnum(const char *sym) const; + + /* TODO: introduce a C++ iterator, but make it nice and templated so that if + * you ask for an iterator of MessageDef the iterated elements are strongly + * typed as MessageDef*. */ + + /* Adds the given mutable defs to the symtab, resolving all symbols + * (including enum default values) and finalizing the defs. Only one def per + * name may be in the list, but defs can replace existing defs in the symtab. + * All defs must have a name -- anonymous defs are not allowed. Anonymous + * defs can still be frozen by calling upb_def_freeze() directly. + * + * Any existing defs that can reach defs that are being replaced will + * themselves be replaced also, so that the resulting set of defs is fully + * consistent. + * + * This logic implemented in this method is a convenience; ultimately it + * calls some combination of upb_fielddef_setsubdef(), upb_def_dup(), and + * upb_freeze(), any of which the client could call themself. However, since + * the logic for doing so is nontrivial, we provide it here. + * + * The entire operation either succeeds or fails. If the operation fails, + * the symtab is unchanged, false is returned, and status indicates the + * error. The caller passes a ref on all defs to the symtab (even if the + * operation fails). + * + * TODO(haberman): currently failure will leave the symtab unchanged, but may + * leave the defs themselves partially resolved. Does this matter? If so we + * could do a prepass that ensures that all symbols are resolvable and bail + * if not, so we don't mutate anything until we know the operation will + * succeed. + * + * TODO(haberman): since the defs must be mutable, refining a frozen def + * requires making mutable copies of the entire tree. This is wasteful if + * only a few messages are changing. We may want to add a way of adding a + * tree of frozen defs to the symtab (perhaps an alternate constructor where + * you pass the root of the tree?) */ + bool Add(Def*const* defs, int n, void* ref_donor, upb_status* status); + + bool Add(const std::vector& defs, void *owner, Status* status) { + return Add((Def*const*)&defs[0], defs.size(), owner, status); + } + + private: + UPB_DISALLOW_POD_OPS(SymbolTable, upb::SymbolTable) +}; + +#endif /* __cplusplus */ + +UPB_BEGIN_EXTERN_C + +/* Native C API. */ + +/* Include refcounted methods like upb_symtab_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_symtab, upb_symtab_upcast) + +upb_symtab *upb_symtab_new(const void *owner); +void upb_symtab_freeze(upb_symtab *s); +const upb_def *upb_symtab_resolve(const upb_symtab *s, const char *base, + const char *sym); +const upb_def *upb_symtab_lookup(const upb_symtab *s, const char *sym); +const upb_msgdef *upb_symtab_lookupmsg(const upb_symtab *s, const char *sym); +const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym); +bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, + upb_status *status); + +/* upb_symtab_iter i; + * for(upb_symtab_begin(&i, s, type); !upb_symtab_done(&i); + * upb_symtab_next(&i)) { + * const upb_def *def = upb_symtab_iter_def(&i); + * // ... + * } + * + * For C we don't have separate iterators for const and non-const. + * It is the caller's responsibility to cast the upb_fielddef* to + * const if the upb_msgdef* is const. */ +void upb_symtab_begin(upb_symtab_iter *iter, const upb_symtab *s, + upb_deftype_t type); +void upb_symtab_next(upb_symtab_iter *iter); +bool upb_symtab_done(const upb_symtab_iter *iter); +const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter); + +UPB_END_EXTERN_C + +#ifdef __cplusplus +/* C++ inline wrappers. */ +namespace upb { +inline reffed_ptr SymbolTable::New() { + upb_symtab *s = upb_symtab_new(&s); + return reffed_ptr(s, &s); +} + +inline void SymbolTable::Freeze() { + return upb_symtab_freeze(this); +} +inline const Def *SymbolTable::Resolve(const char *base, + const char *sym) const { + return upb_symtab_resolve(this, base, sym); +} +inline const Def* SymbolTable::Lookup(const char *sym) const { + return upb_symtab_lookup(this, sym); +} +inline const MessageDef *SymbolTable::LookupMessage(const char *sym) const { + return upb_symtab_lookupmsg(this, sym); +} +inline bool SymbolTable::Add( + Def*const* defs, int n, void* ref_donor, upb_status* status) { + return upb_symtab_add(this, (upb_def*const*)defs, n, ref_donor, status); +} +} /* namespace upb */ +#endif + +#endif /* UPB_SYMTAB_H_ */ +/* +** upb::descriptor::Reader (upb_descreader) +** +** Provides a way of building upb::Defs from data in descriptor.proto format. +*/ + +#ifndef UPB_DESCRIPTOR_H +#define UPB_DESCRIPTOR_H + + +#ifdef __cplusplus +namespace upb { +namespace descriptor { +class Reader; +} /* namespace descriptor */ +} /* namespace upb */ +#endif + +UPB_DECLARE_TYPE(upb::descriptor::Reader, upb_descreader) + +#ifdef __cplusplus + +/* Class that receives descriptor data according to the descriptor.proto schema + * and use it to build upb::Defs corresponding to that schema. */ +class upb::descriptor::Reader { + public: + /* These handlers must have come from NewHandlers() and must outlive the + * Reader. + * + * TODO: generate the handlers statically (like we do with the + * descriptor.proto defs) so that there is no need to pass this parameter (or + * to build/memory-manage the handlers at runtime at all). Unfortunately this + * is a bit tricky to implement for Handlers, but necessary to simplify this + * interface. */ + static Reader* Create(Environment* env, const Handlers* handlers); + + /* The reader's input; this is where descriptor.proto data should be sent. */ + Sink* input(); + + /* Returns an array of all defs that have been parsed, and transfers ownership + * of them to "owner". The number of defs is stored in *n. Ownership of the + * returned array is retained and is invalidated by any other call into + * Reader. + * + * These defs are not frozen or resolved; they are ready to be added to a + * symtab. */ + upb::Def** GetDefs(void* owner, int* n); + + /* Builds and returns handlers for the reader, owned by "owner." */ + static Handlers* NewHandlers(const void* owner); + + private: + UPB_DISALLOW_POD_OPS(Reader, upb::descriptor::Reader) +}; + +#endif + +UPB_BEGIN_EXTERN_C + +/* C API. */ +upb_descreader *upb_descreader_create(upb_env *e, const upb_handlers *h); +upb_sink *upb_descreader_input(upb_descreader *r); +upb_def **upb_descreader_getdefs(upb_descreader *r, void *owner, int *n); +const upb_handlers *upb_descreader_newhandlers(const void *owner); + +UPB_END_EXTERN_C + +#ifdef __cplusplus +/* C++ implementation details. ************************************************/ +namespace upb { +namespace descriptor { +inline Reader* Reader::Create(Environment* e, const Handlers *h) { + return upb_descreader_create(e, h); +} +inline Sink* Reader::input() { return upb_descreader_input(this); } +inline upb::Def** Reader::GetDefs(void* owner, int* n) { + return upb_descreader_getdefs(this, owner, n); +} +} /* namespace descriptor */ +} /* namespace upb */ +#endif + +#endif /* UPB_DESCRIPTOR_H */ +/* This file contains accessors for a set of compiled-in defs. + * Note that unlike Google's protobuf, it does *not* define + * generated classes or any other kind of data structure for + * actually storing protobufs. It only contains *defs* which + * let you reflect over a protobuf *schema*. + */ +/* This file was generated by upbc (the upb compiler). + * Do not edit -- your changes will be discarded when the file is + * regenerated. */ + +#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ +#define GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ + + +#ifdef __cplusplus +UPB_BEGIN_EXTERN_C +#endif + +/* Enums */ + +typedef enum { + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_OPTIONAL = 1, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_REQUIRED = 2, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_REPEATED = 3 +} google_protobuf_FieldDescriptorProto_Label; + +typedef enum { + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_DOUBLE = 1, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FLOAT = 2, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT64 = 3, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_UINT64 = 4, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT32 = 5, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FIXED64 = 6, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FIXED32 = 7, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_BOOL = 8, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_STRING = 9, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_GROUP = 10, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_MESSAGE = 11, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_BYTES = 12, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_UINT32 = 13, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_ENUM = 14, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SFIXED32 = 15, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SFIXED64 = 16, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SINT32 = 17, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SINT64 = 18 +} google_protobuf_FieldDescriptorProto_Type; + +typedef enum { + GOOGLE_PROTOBUF_FIELDOPTIONS_STRING = 0, + GOOGLE_PROTOBUF_FIELDOPTIONS_CORD = 1, + GOOGLE_PROTOBUF_FIELDOPTIONS_STRING_PIECE = 2 +} google_protobuf_FieldOptions_CType; + +typedef enum { + GOOGLE_PROTOBUF_FILEOPTIONS_SPEED = 1, + GOOGLE_PROTOBUF_FILEOPTIONS_CODE_SIZE = 2, + GOOGLE_PROTOBUF_FILEOPTIONS_LITE_RUNTIME = 3 +} google_protobuf_FileOptions_OptimizeMode; + +/* Selectors */ + +/* google.protobuf.DescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_STARTSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_STARTSUBMSG 4 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_STARTSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_STARTSUBMSG 6 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_OPTIONS_STARTSUBMSG 7 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_STARTSEQ 8 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_ENDSEQ 9 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_ENDSUBMSG 10 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_STARTSEQ 11 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_ENDSEQ 12 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_ENDSUBMSG 13 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_STARTSEQ 14 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_ENDSEQ 15 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_ENDSUBMSG 16 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_STARTSEQ 17 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_ENDSEQ 18 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_ENDSUBMSG 19 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_STARTSEQ 20 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_ENDSEQ 21 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_ENDSUBMSG 22 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_OPTIONS_ENDSUBMSG 23 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_STRING 24 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_STARTSTR 25 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_ENDSTR 26 + +/* google.protobuf.DescriptorProto.ExtensionRange */ +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSIONRANGE_START_INT32 2 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSIONRANGE_END_INT32 3 + +/* google.protobuf.EnumDescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_STARTSEQ 4 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_ENDSEQ 5 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_ENDSUBMSG 6 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 7 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_STRING 8 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_STARTSTR 9 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_ENDSTR 10 + +/* google.protobuf.EnumOptions */ +#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_ALLOW_ALIAS_BOOL 6 + +/* google.protobuf.EnumValueDescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_STRING 4 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_STARTSTR 5 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_ENDSTR 6 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NUMBER_INT32 7 + +/* google.protobuf.EnumValueOptions */ +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 + +/* google.protobuf.FieldDescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_STRING 4 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_STARTSTR 5 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_ENDSTR 6 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_STRING 7 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_STARTSTR 8 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_ENDSTR 9 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NUMBER_INT32 10 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_INT32 11 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT32 12 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_STRING 13 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_STARTSTR 14 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_ENDSTR 15 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_STRING 16 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_STARTSTR 17 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_ENDSTR 18 + +/* google.protobuf.FieldOptions */ +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_CTYPE_INT32 6 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_PACKED_BOOL 7 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_DEPRECATED_BOOL 8 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_LAZY_BOOL 9 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_EXPERIMENTAL_MAP_KEY_STRING 10 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_EXPERIMENTAL_MAP_KEY_STARTSTR 11 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_EXPERIMENTAL_MAP_KEY_ENDSTR 12 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_WEAK_BOOL 13 + +/* google.protobuf.FileDescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_STARTSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_STARTSUBMSG 4 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_STARTSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 6 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SOURCE_CODE_INFO_STARTSUBMSG 7 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_STARTSEQ 8 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_ENDSEQ 9 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_ENDSUBMSG 10 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_STARTSEQ 11 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_ENDSEQ 12 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_ENDSUBMSG 13 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_STARTSEQ 14 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_ENDSEQ 15 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_ENDSUBMSG 16 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_STARTSEQ 17 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_ENDSEQ 18 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_ENDSUBMSG 19 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 20 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SOURCE_CODE_INFO_ENDSUBMSG 21 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_STRING 22 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_STARTSTR 23 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_ENDSTR 24 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_STRING 25 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_STARTSTR 26 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_ENDSTR 27 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STARTSEQ 28 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_ENDSEQ 29 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STRING 30 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STARTSTR 31 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_ENDSTR 32 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_STARTSEQ 33 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_ENDSEQ 34 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_INT32 35 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_STARTSEQ 36 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_ENDSEQ 37 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_INT32 38 + +/* google.protobuf.FileDescriptorSet */ +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_ENDSUBMSG 5 + +/* google.protobuf.FileOptions */ +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_STRING 6 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_STARTSTR 7 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_ENDSTR 8 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_STRING 9 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_STARTSTR 10 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_ENDSTR 11 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_OPTIMIZE_FOR_INT32 12 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_MULTIPLE_FILES_BOOL 13 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_STRING 14 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_STARTSTR 15 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_ENDSTR 16 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_CC_GENERIC_SERVICES_BOOL 17 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_GENERIC_SERVICES_BOOL 18 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_PY_GENERIC_SERVICES_BOOL 19 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_GENERATE_EQUALS_AND_HASH_BOOL 20 + +/* google.protobuf.MessageOptions */ +#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_MESSAGE_SET_WIRE_FORMAT_BOOL 6 +#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_NO_STANDARD_DESCRIPTOR_ACCESSOR_BOOL 7 + +/* google.protobuf.MethodDescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_STRING 4 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_STARTSTR 5 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_ENDSTR 6 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_STRING 7 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_STARTSTR 8 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_ENDSTR 9 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_STRING 10 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_STARTSTR 11 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_ENDSTR 12 + +/* google.protobuf.MethodOptions */ +#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 + +/* google.protobuf.ServiceDescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_STARTSEQ 4 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_ENDSEQ 5 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_ENDSUBMSG 6 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 7 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_STRING 8 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_STARTSTR 9 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_ENDSTR 10 + +/* google.protobuf.ServiceOptions */ +#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 + +/* google.protobuf.SourceCodeInfo */ +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_ENDSUBMSG 5 + +/* google.protobuf.SourceCodeInfo.Location */ +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_STARTSEQ 2 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_ENDSEQ 3 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_INT32 4 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_STARTSEQ 5 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_ENDSEQ 6 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_INT32 7 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_STRING 8 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_STARTSTR 9 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_ENDSTR 10 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_STRING 11 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_STARTSTR 12 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_ENDSTR 13 + +/* google.protobuf.UninterpretedOption */ +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_ENDSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_STRING 6 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_STARTSTR 7 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_ENDSTR 8 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_POSITIVE_INT_VALUE_UINT64 9 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NEGATIVE_INT_VALUE_INT64 10 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_DOUBLE_VALUE_DOUBLE 11 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_STRING 12 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_STARTSTR 13 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_ENDSTR 14 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_STRING 15 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_STARTSTR 16 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_ENDSTR 17 + +/* google.protobuf.UninterpretedOption.NamePart */ +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_STRING 2 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_STARTSTR 3 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_ENDSTR 4 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_IS_EXTENSION_BOOL 5 + +const upb_symtab *upbdefs_google_protobuf_descriptor(const void *owner); + +/* MessageDefs */ +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_DescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto.ExtensionRange"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumDescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumDescriptorProto"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumOptions"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumValueDescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumValueDescriptorProto"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumValueOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumValueOptions"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FieldDescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FieldDescriptorProto"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FieldOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FieldOptions"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileDescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileDescriptorProto"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileDescriptorSet(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileDescriptorSet"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileOptions"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MessageOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MessageOptions"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MethodDescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MethodDescriptorProto"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MethodOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MethodOptions"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_ServiceDescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.ServiceDescriptorProto"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_ServiceOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.ServiceOptions"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.SourceCodeInfo"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo_Location(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.SourceCodeInfo.Location"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.UninterpretedOption"); + assert(m); + return m; +} +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption_NamePart(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.UninterpretedOption.NamePart"); + assert(m); + return m; +} + + +/* EnumDefs */ +UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Label(const upb_symtab *s) { + const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldDescriptorProto.Label"); + assert(e); + return e; +} +UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Type(const upb_symtab *s) { + const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldDescriptorProto.Type"); + assert(e); + return e; +} +UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldOptions_CType(const upb_symtab *s) { + const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldOptions.CType"); + assert(e); + return e; +} +UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FileOptions_OptimizeMode(const upb_symtab *s) { + const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FileOptions.OptimizeMode"); + assert(e); + return e; +} + +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_end(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto_ExtensionRange(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_start(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto_ExtensionRange(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_enum_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_extension_range(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_field(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_nested_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 7); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_allow_alias(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumOptions(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_number(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_default_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 7); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_extendee(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_label(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_number(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 8); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_type_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_ctype(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_deprecated(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_experimental_map_key(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 9); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_lazy(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_packed(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_weak(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 10); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_enum_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 7); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_message_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 8); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_public_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 10); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_service(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_source_code_info(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 9); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_weak_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 11); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorSet_file(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorSet(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_cc_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 16); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_go_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 11); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_generate_equals_and_hash(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 20); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 17); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_multiple_files(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 10); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_outer_classname(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 8); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_optimize_for(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 9); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_py_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 18); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_message_set_wire_format(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_no_standard_descriptor_accessor(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_input_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_output_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_method(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_leading_comments(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_path(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_span(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_trailing_comments(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_location(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_is_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption_NamePart(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_name_part(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption_NamePart(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_aggregate_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 8); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_double_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_identifier_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_negative_int_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_positive_int_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_string_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 7); } + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +namespace upbdefs { +namespace google { +namespace protobuf { +namespace descriptor { +inline upb::reffed_ptr SymbolTable() { + const upb::SymbolTable* s = upbdefs_google_protobuf_descriptor(&s); + return upb::reffed_ptr(s, &s); +} +} /* namespace descriptor */ +} /* namespace protobuf */ +} /* namespace google */ + +#define RETURN_REFFED(type, func) \ + const type* obj = func(upbdefs::google::protobuf::descriptor::SymbolTable().get()); \ + return upb::reffed_ptr(obj); + +namespace google { +namespace protobuf { +namespace DescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_DescriptorProto) } +inline upb::reffed_ptr enum_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_enum_type) } +inline upb::reffed_ptr extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_extension) } +inline upb::reffed_ptr extension_range() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_extension_range) } +inline upb::reffed_ptr field() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_field) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_name) } +inline upb::reffed_ptr nested_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_nested_type) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_options) } +} /* namespace DescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace DescriptorProto { +namespace ExtensionRange { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange) } +inline upb::reffed_ptr end() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange_end) } +inline upb::reffed_ptr start() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange_start) } +} /* namespace ExtensionRange */ +} /* namespace DescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace EnumDescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumDescriptorProto) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_name) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_options) } +inline upb::reffed_ptr value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_value) } +} /* namespace EnumDescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace EnumOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumOptions) } +inline upb::reffed_ptr allow_alias() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumOptions_allow_alias) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumOptions_uninterpreted_option) } +} /* namespace EnumOptions */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace EnumValueDescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumValueDescriptorProto) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_name) } +inline upb::reffed_ptr number() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_number) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_options) } +} /* namespace EnumValueDescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace EnumValueOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumValueOptions) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueOptions_uninterpreted_option) } +} /* namespace EnumValueOptions */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace FieldDescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FieldDescriptorProto) } +inline upb::reffed_ptr default_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_default_value) } +inline upb::reffed_ptr extendee() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_extendee) } +inline upb::reffed_ptr label() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_label) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_name) } +inline upb::reffed_ptr number() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_number) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_options) } +inline upb::reffed_ptr type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_type) } +inline upb::reffed_ptr type_name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_type_name) } +inline upb::reffed_ptr Label() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldDescriptorProto_Label) } +inline upb::reffed_ptr Type() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldDescriptorProto_Type) } +} /* namespace FieldDescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace FieldOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FieldOptions) } +inline upb::reffed_ptr ctype() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_ctype) } +inline upb::reffed_ptr deprecated() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_deprecated) } +inline upb::reffed_ptr experimental_map_key() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_experimental_map_key) } +inline upb::reffed_ptr lazy() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_lazy) } +inline upb::reffed_ptr packed() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_packed) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_uninterpreted_option) } +inline upb::reffed_ptr weak() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_weak) } +inline upb::reffed_ptr CType() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldOptions_CType) } +} /* namespace FieldOptions */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace FileDescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileDescriptorProto) } +inline upb::reffed_ptr dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_dependency) } +inline upb::reffed_ptr enum_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_enum_type) } +inline upb::reffed_ptr extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_extension) } +inline upb::reffed_ptr message_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_message_type) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_name) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_options) } +inline upb::reffed_ptr package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_package) } +inline upb::reffed_ptr public_dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_public_dependency) } +inline upb::reffed_ptr service() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_service) } +inline upb::reffed_ptr source_code_info() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_source_code_info) } +inline upb::reffed_ptr weak_dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_weak_dependency) } +} /* namespace FileDescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace FileDescriptorSet { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileDescriptorSet) } +inline upb::reffed_ptr file() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorSet_file) } +} /* namespace FileDescriptorSet */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace FileOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileOptions) } +inline upb::reffed_ptr cc_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_cc_generic_services) } +inline upb::reffed_ptr go_package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_go_package) } +inline upb::reffed_ptr java_generate_equals_and_hash() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_generate_equals_and_hash) } +inline upb::reffed_ptr java_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_generic_services) } +inline upb::reffed_ptr java_multiple_files() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_multiple_files) } +inline upb::reffed_ptr java_outer_classname() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_outer_classname) } +inline upb::reffed_ptr java_package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_package) } +inline upb::reffed_ptr optimize_for() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_optimize_for) } +inline upb::reffed_ptr py_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_py_generic_services) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_uninterpreted_option) } +inline upb::reffed_ptr OptimizeMode() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FileOptions_OptimizeMode) } +} /* namespace FileOptions */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace MessageOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MessageOptions) } +inline upb::reffed_ptr message_set_wire_format() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_message_set_wire_format) } +inline upb::reffed_ptr no_standard_descriptor_accessor() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_no_standard_descriptor_accessor) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_uninterpreted_option) } +} /* namespace MessageOptions */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace MethodDescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MethodDescriptorProto) } +inline upb::reffed_ptr input_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_input_type) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_name) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_options) } +inline upb::reffed_ptr output_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_output_type) } +} /* namespace MethodDescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace MethodOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MethodOptions) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodOptions_uninterpreted_option) } +} /* namespace MethodOptions */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace ServiceDescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_ServiceDescriptorProto) } +inline upb::reffed_ptr method() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_method) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_name) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_options) } +} /* namespace ServiceDescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace ServiceOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_ServiceOptions) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceOptions_uninterpreted_option) } +} /* namespace ServiceOptions */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace SourceCodeInfo { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_SourceCodeInfo) } +inline upb::reffed_ptr location() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_location) } +} /* namespace SourceCodeInfo */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace SourceCodeInfo { +namespace Location { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_SourceCodeInfo_Location) } +inline upb::reffed_ptr leading_comments() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_leading_comments) } +inline upb::reffed_ptr path() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_path) } +inline upb::reffed_ptr span() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_span) } +inline upb::reffed_ptr trailing_comments() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_trailing_comments) } +} /* namespace Location */ +} /* namespace SourceCodeInfo */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace UninterpretedOption { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_UninterpretedOption) } +inline upb::reffed_ptr aggregate_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_aggregate_value) } +inline upb::reffed_ptr double_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_double_value) } +inline upb::reffed_ptr identifier_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_identifier_value) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_name) } +inline upb::reffed_ptr negative_int_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_negative_int_value) } +inline upb::reffed_ptr positive_int_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_positive_int_value) } +inline upb::reffed_ptr string_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_string_value) } +} /* namespace UninterpretedOption */ +} /* namespace protobuf */ +} /* namespace google */ + +namespace google { +namespace protobuf { +namespace UninterpretedOption { +namespace NamePart { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_UninterpretedOption_NamePart) } +inline upb::reffed_ptr is_extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_NamePart_is_extension) } +inline upb::reffed_ptr name_part() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_NamePart_name_part) } +} /* namespace NamePart */ +} /* namespace UninterpretedOption */ +} /* namespace protobuf */ +} /* namespace google */ + +} /* namespace upbdefs */ + + +#undef RETURN_REFFED +#endif /* __cplusplus */ + +#endif /* GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ */ +/* +** Internal-only definitions for the decoder. +*/ + +#ifndef UPB_DECODER_INT_H_ +#define UPB_DECODER_INT_H_ + +#include +/* +** upb::pb::Decoder +** +** A high performance, streaming, resumable decoder for the binary protobuf +** format. +** +** This interface works the same regardless of what decoder backend is being +** used. A client of this class does not need to know whether decoding is using +** a JITted decoder (DynASM, LLVM, etc) or an interpreted decoder. By default, +** it will always use the fastest available decoder. However, you can call +** set_allow_jit(false) to disable any JIT decoder that might be available. +** This is primarily useful for testing purposes. +*/ + +#ifndef UPB_DECODER_H_ +#define UPB_DECODER_H_ + + +#ifdef __cplusplus +namespace upb { +namespace pb { +class CodeCache; +class Decoder; +class DecoderMethod; +class DecoderMethodOptions; +} /* namespace pb */ +} /* namespace upb */ +#endif + +UPB_DECLARE_TYPE(upb::pb::CodeCache, upb_pbcodecache) +UPB_DECLARE_TYPE(upb::pb::Decoder, upb_pbdecoder) +UPB_DECLARE_TYPE(upb::pb::DecoderMethodOptions, upb_pbdecodermethodopts) + +UPB_DECLARE_DERIVED_TYPE(upb::pb::DecoderMethod, upb::RefCounted, + upb_pbdecodermethod, upb_refcounted) + +#ifdef __cplusplus + +/* The parameters one uses to construct a DecoderMethod. + * TODO(haberman): move allowjit here? Seems more convenient for users. + * TODO(haberman): move this to be heap allocated for ABI stability. */ +class upb::pb::DecoderMethodOptions { + public: + /* Parameter represents the destination handlers that this method will push + * to. */ + explicit DecoderMethodOptions(const Handlers* dest_handlers); + + /* Should the decoder push submessages to lazy handlers for fields that have + * them? The caller should set this iff the lazy handlers expect data that is + * in protobuf binary format and the caller wishes to lazy parse it. */ + void set_lazy(bool lazy); +#else +struct upb_pbdecodermethodopts { +#endif + const upb_handlers *handlers; + bool lazy; +}; + +#ifdef __cplusplus + +/* Represents the code to parse a protobuf according to a destination + * Handlers. */ +class upb::pb::DecoderMethod { + public: + /* Include base methods from upb::ReferenceCounted. */ + UPB_REFCOUNTED_CPPMETHODS + + /* The destination handlers that are statically bound to this method. + * This method is only capable of outputting to a sink that uses these + * handlers. */ + const Handlers* dest_handlers() const; + + /* The input handlers for this decoder method. */ + const BytesHandler* input_handler() const; + + /* Whether this method is native. */ + bool is_native() const; + + /* Convenience method for generating a DecoderMethod without explicitly + * creating a CodeCache. */ + static reffed_ptr New(const DecoderMethodOptions& opts); + + private: + UPB_DISALLOW_POD_OPS(DecoderMethod, upb::pb::DecoderMethod) +}; + +#endif + +/* Preallocation hint: decoder won't allocate more bytes than this when first + * constructed. This hint may be an overestimate for some build configurations. + * But if the decoder library is upgraded without recompiling the application, + * it may be an underestimate. */ +#define UPB_PB_DECODER_SIZE 4408 + +#ifdef __cplusplus + +/* A Decoder receives binary protobuf data on its input sink and pushes the + * decoded data to its output sink. */ +class upb::pb::Decoder { + public: + /* Constructs a decoder instance for the given method, which must outlive this + * decoder. Any errors during parsing will be set on the given status, which + * must also outlive this decoder. + * + * The sink must match the given method. */ + static Decoder* Create(Environment* env, const DecoderMethod* method, + Sink* output); + + /* Returns the DecoderMethod this decoder is parsing from. */ + const DecoderMethod* method() const; + + /* The sink on which this decoder receives input. */ + BytesSink* input(); + + /* Returns number of bytes successfully parsed. + * + * This can be useful for determining the stream position where an error + * occurred. + * + * This value may not be up-to-date when called from inside a parsing + * callback. */ + uint64_t BytesParsed() const; + + /* Gets/sets the parsing nexting limit. If the total number of nested + * submessages and repeated fields hits this limit, parsing will fail. This + * is a resource limit that controls the amount of memory used by the parsing + * stack. + * + * Setting the limit will fail if the parser is currently suspended at a depth + * greater than this, or if memory allocation of the stack fails. */ + size_t max_nesting() const; + bool set_max_nesting(size_t max); + + void Reset(); + + static const size_t kSize = UPB_PB_DECODER_SIZE; + + private: + UPB_DISALLOW_POD_OPS(Decoder, upb::pb::Decoder) +}; + +#endif /* __cplusplus */ + +#ifdef __cplusplus + +/* A class for caching protobuf processing code, whether bytecode for the + * interpreted decoder or machine code for the JIT. + * + * This class is not thread-safe. + * + * TODO(haberman): move this to be heap allocated for ABI stability. */ +class upb::pb::CodeCache { + public: + CodeCache(); + ~CodeCache(); + + /* Whether the cache is allowed to generate machine code. Defaults to true. + * There is no real reason to turn it off except for testing or if you are + * having a specific problem with the JIT. + * + * Note that allow_jit = true does not *guarantee* that the code will be JIT + * compiled. If this platform is not supported or the JIT was not compiled + * in, the code may still be interpreted. */ + bool allow_jit() const; + + /* This may only be called when the object is first constructed, and prior to + * any code generation, otherwise returns false and does nothing. */ + bool set_allow_jit(bool allow); + + /* Returns a DecoderMethod that can push data to the given handlers. + * If a suitable method already exists, it will be returned from the cache. + * + * Specifying the destination handlers here allows the DecoderMethod to be + * statically bound to the destination handlers if possible, which can allow + * more efficient decoding. However the returned method may or may not + * actually be statically bound. But in all cases, the returned method can + * push data to the given handlers. */ + const DecoderMethod *GetDecoderMethod(const DecoderMethodOptions& opts); + + /* If/when someone needs to explicitly create a dynamically-bound + * DecoderMethod*, we can add a method to get it here. */ + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(CodeCache) +#else +struct upb_pbcodecache { +#endif + bool allow_jit_; + + /* Array of mgroups. */ + upb_inttable groups; +}; + +UPB_BEGIN_EXTERN_C + +upb_pbdecoder *upb_pbdecoder_create(upb_env *e, + const upb_pbdecodermethod *method, + upb_sink *output); +const upb_pbdecodermethod *upb_pbdecoder_method(const upb_pbdecoder *d); +upb_bytessink *upb_pbdecoder_input(upb_pbdecoder *d); +uint64_t upb_pbdecoder_bytesparsed(const upb_pbdecoder *d); +size_t upb_pbdecoder_maxnesting(const upb_pbdecoder *d); +bool upb_pbdecoder_setmaxnesting(upb_pbdecoder *d, size_t max); +void upb_pbdecoder_reset(upb_pbdecoder *d); + +void upb_pbdecodermethodopts_init(upb_pbdecodermethodopts *opts, + const upb_handlers *h); +void upb_pbdecodermethodopts_setlazy(upb_pbdecodermethodopts *opts, bool lazy); + + +/* Include refcounted methods like upb_pbdecodermethod_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_pbdecodermethod, upb_pbdecodermethod_upcast) + +const upb_handlers *upb_pbdecodermethod_desthandlers( + const upb_pbdecodermethod *m); +const upb_byteshandler *upb_pbdecodermethod_inputhandler( + const upb_pbdecodermethod *m); +bool upb_pbdecodermethod_isnative(const upb_pbdecodermethod *m); +const upb_pbdecodermethod *upb_pbdecodermethod_new( + const upb_pbdecodermethodopts *opts, const void *owner); + +void upb_pbcodecache_init(upb_pbcodecache *c); +void upb_pbcodecache_uninit(upb_pbcodecache *c); +bool upb_pbcodecache_allowjit(const upb_pbcodecache *c); +bool upb_pbcodecache_setallowjit(upb_pbcodecache *c, bool allow); +const upb_pbdecodermethod *upb_pbcodecache_getdecodermethod( + upb_pbcodecache *c, const upb_pbdecodermethodopts *opts); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +namespace upb { + +namespace pb { + +/* static */ +inline Decoder* Decoder::Create(Environment* env, const DecoderMethod* m, + Sink* sink) { + return upb_pbdecoder_create(env, m, sink); +} +inline const DecoderMethod* Decoder::method() const { + return upb_pbdecoder_method(this); +} +inline BytesSink* Decoder::input() { + return upb_pbdecoder_input(this); +} +inline uint64_t Decoder::BytesParsed() const { + return upb_pbdecoder_bytesparsed(this); +} +inline size_t Decoder::max_nesting() const { + return upb_pbdecoder_maxnesting(this); +} +inline bool Decoder::set_max_nesting(size_t max) { + return upb_pbdecoder_setmaxnesting(this, max); +} +inline void Decoder::Reset() { upb_pbdecoder_reset(this); } + +inline DecoderMethodOptions::DecoderMethodOptions(const Handlers* h) { + upb_pbdecodermethodopts_init(this, h); +} +inline void DecoderMethodOptions::set_lazy(bool lazy) { + upb_pbdecodermethodopts_setlazy(this, lazy); +} + +inline const Handlers* DecoderMethod::dest_handlers() const { + return upb_pbdecodermethod_desthandlers(this); +} +inline const BytesHandler* DecoderMethod::input_handler() const { + return upb_pbdecodermethod_inputhandler(this); +} +inline bool DecoderMethod::is_native() const { + return upb_pbdecodermethod_isnative(this); +} +/* static */ +inline reffed_ptr DecoderMethod::New( + const DecoderMethodOptions &opts) { + const upb_pbdecodermethod *m = upb_pbdecodermethod_new(&opts, &m); + return reffed_ptr(m, &m); +} + +inline CodeCache::CodeCache() { + upb_pbcodecache_init(this); +} +inline CodeCache::~CodeCache() { + upb_pbcodecache_uninit(this); +} +inline bool CodeCache::allow_jit() const { + return upb_pbcodecache_allowjit(this); +} +inline bool CodeCache::set_allow_jit(bool allow) { + return upb_pbcodecache_setallowjit(this, allow); +} +inline const DecoderMethod *CodeCache::GetDecoderMethod( + const DecoderMethodOptions& opts) { + return upb_pbcodecache_getdecodermethod(this, &opts); +} + +} /* namespace pb */ +} /* namespace upb */ + +#endif /* __cplusplus */ + +#endif /* UPB_DECODER_H_ */ + +/* C++ names are not actually used since this type isn't exposed to users. */ +#ifdef __cplusplus +namespace upb { +namespace pb { +class MessageGroup; +} /* namespace pb */ +} /* namespace upb */ +#endif +UPB_DECLARE_DERIVED_TYPE(upb::pb::MessageGroup, upb::RefCounted, + mgroup, upb_refcounted) + +/* Opcode definitions. The canonical meaning of each opcode is its + * implementation in the interpreter (the JIT is written to match this). + * + * All instructions have the opcode in the low byte. + * Instruction format for most instructions is: + * + * +-------------------+--------+ + * | arg (24) | op (8) | + * +-------------------+--------+ + * + * Exceptions are indicated below. A few opcodes are multi-word. */ +typedef enum { + /* Opcodes 1-8, 13, 15-18 parse their respective descriptor types. + * Arg for all of these is the upb selector for this field. */ +#define T(type) OP_PARSE_ ## type = UPB_DESCRIPTOR_TYPE_ ## type + T(DOUBLE), T(FLOAT), T(INT64), T(UINT64), T(INT32), T(FIXED64), T(FIXED32), + T(BOOL), T(UINT32), T(SFIXED32), T(SFIXED64), T(SINT32), T(SINT64), +#undef T + OP_STARTMSG = 9, /* No arg. */ + OP_ENDMSG = 10, /* No arg. */ + OP_STARTSEQ = 11, + OP_ENDSEQ = 12, + OP_STARTSUBMSG = 14, + OP_ENDSUBMSG = 19, + OP_STARTSTR = 20, + OP_STRING = 21, + OP_ENDSTR = 22, + + OP_PUSHTAGDELIM = 23, /* No arg. */ + OP_PUSHLENDELIM = 24, /* No arg. */ + OP_POP = 25, /* No arg. */ + OP_SETDELIM = 26, /* No arg. */ + OP_SETBIGGROUPNUM = 27, /* two words: + * | unused (24) | opc (8) | + * | groupnum (32) | */ + OP_CHECKDELIM = 28, + OP_CALL = 29, + OP_RET = 30, + OP_BRANCH = 31, + + /* Different opcodes depending on how many bytes expected. */ + OP_TAG1 = 32, /* | match tag (16) | jump target (8) | opc (8) | */ + OP_TAG2 = 33, /* | match tag (16) | jump target (8) | opc (8) | */ + OP_TAGN = 34, /* three words: */ + /* | unused (16) | jump target(8) | opc (8) | */ + /* | match tag 1 (32) | */ + /* | match tag 2 (32) | */ + + OP_SETDISPATCH = 35, /* N words: */ + /* | unused (24) | opc | */ + /* | upb_inttable* (32 or 64) | */ + + OP_DISPATCH = 36, /* No arg. */ + + OP_HALT = 37 /* No arg. */ +} opcode; + +#define OP_MAX OP_HALT + +UPB_INLINE opcode getop(uint32_t instr) { return instr & 0xff; } + +/* Method group; represents a set of decoder methods that had their code + * emitted together, and must therefore be freed together. Immutable once + * created. It is possible we may want to expose this to users at some point. + * + * Overall ownership of Decoder objects looks like this: + * + * +----------+ + * | | <---> DecoderMethod + * | method | + * CodeCache ---> | group | <---> DecoderMethod + * | | + * | (mgroup) | <---> DecoderMethod + * +----------+ + */ +struct mgroup { + upb_refcounted base; + + /* Maps upb_msgdef/upb_handlers -> upb_pbdecodermethod. We own refs on the + * methods. */ + upb_inttable methods; + + /* When we add the ability to link to previously existing mgroups, we'll + * need an array of mgroups we reference here, and own refs on them. */ + + /* The bytecode for our methods, if any exists. Owned by us. */ + uint32_t *bytecode; + uint32_t *bytecode_end; + +#ifdef UPB_USE_JIT_X64 + /* JIT-generated machine code, if any. */ + upb_string_handlerfunc *jit_code; + /* The size of the jit_code (required to munmap()). */ + size_t jit_size; + char *debug_info; + void *dl; +#endif +}; + +/* The maximum that any submessages can be nested. Matches proto2's limit. + * This specifies the size of the decoder's statically-sized array and therefore + * setting it high will cause the upb::pb::Decoder object to be larger. + * + * If necessary we can add a runtime-settable property to Decoder that allow + * this to be larger than the compile-time setting, but this would add + * complexity, particularly since we would have to decide how/if to give users + * the ability to set a custom memory allocation function. */ +#define UPB_DECODER_MAX_NESTING 64 + +/* Internal-only struct used by the decoder. */ +typedef struct { + /* Space optimization note: we store two pointers here that the JIT + * doesn't need at all; the upb_handlers* inside the sink and + * the dispatch table pointer. We can optimze so that the JIT uses + * smaller stack frames than the interpreter. The only thing we need + * to guarantee is that the fallback routines can find end_ofs. */ + upb_sink sink; + + /* The absolute stream offset of the end-of-frame delimiter. + * Non-delimited frames (groups and non-packed repeated fields) reuse the + * delimiter of their parent, even though the frame may not end there. + * + * NOTE: the JIT stores a slightly different value here for non-top frames. + * It stores the value relative to the end of the enclosed message. But the + * top frame is still stored the same way, which is important for ensuring + * that calls from the JIT into C work correctly. */ + uint64_t end_ofs; + const uint32_t *base; + + /* 0 indicates a length-delimited field. + * A positive number indicates a known group. + * A negative number indicates an unknown group. */ + int32_t groupnum; + upb_inttable *dispatch; /* Not used by the JIT. */ +} upb_pbdecoder_frame; + +struct upb_pbdecodermethod { + upb_refcounted base; + + /* While compiling, the base is relative in "ofs", after compiling it is + * absolute in "ptr". */ + union { + uint32_t ofs; /* PC offset of method. */ + void *ptr; /* Pointer to bytecode or machine code for this method. */ + } code_base; + + /* The decoder method group to which this method belongs. We own a ref. + * Owning a ref on the entire group is more coarse-grained than is strictly + * necessary; all we truly require is that methods we directly reference + * outlive us, while the group could contain many other messages we don't + * require. But the group represents the messages that were + * allocated+compiled together, so it makes the most sense to free them + * together also. */ + const upb_refcounted *group; + + /* Whether this method is native code or bytecode. */ + bool is_native_; + + /* The handler one calls to invoke this method. */ + upb_byteshandler input_handler_; + + /* The destination handlers this method is bound to. We own a ref. */ + const upb_handlers *dest_handlers_; + + /* Dispatch table -- used by both bytecode decoder and JIT when encountering a + * field number that wasn't the one we were expecting to see. See + * decoder.int.h for the layout of this table. */ + upb_inttable dispatch; +}; + +struct upb_pbdecoder { + upb_env *env; + + /* Our input sink. */ + upb_bytessink input_; + + /* The decoder method we are parsing with (owned). */ + const upb_pbdecodermethod *method_; + + size_t call_len; + const uint32_t *pc, *last; + + /* Current input buffer and its stream offset. */ + const char *buf, *ptr, *end, *checkpoint; + + /* End of the delimited region, relative to ptr, NULL if not in this buf. */ + const char *delim_end; + + /* End of the delimited region, relative to ptr, end if not in this buf. */ + const char *data_end; + + /* Overall stream offset of "buf." */ + uint64_t bufstart_ofs; + + /* Buffer for residual bytes not parsed from the previous buffer. + * The maximum number of residual bytes we require is 12; a five-byte + * unknown tag plus an eight-byte value, less one because the value + * is only a partial value. */ + char residual[12]; + char *residual_end; + + /* Bytes of data that should be discarded from the input beore we start + * parsing again. We set this when we internally determine that we can + * safely skip the next N bytes, but this region extends past the current + * user buffer. */ + size_t skip; + + /* Stores the user buffer passed to our decode function. */ + const char *buf_param; + size_t size_param; + const upb_bufhandle *handle; + + /* Our internal stack. */ + upb_pbdecoder_frame *stack, *top, *limit; + const uint32_t **callstack; + size_t stack_size; + + upb_status *status; + +#ifdef UPB_USE_JIT_X64 + /* Used momentarily by the generated code to store a value while a user + * function is called. */ + uint32_t tmp_len; + + const void *saved_rsp; +#endif +}; + +/* Decoder entry points; used as handlers. */ +void *upb_pbdecoder_startbc(void *closure, const void *pc, size_t size_hint); +void *upb_pbdecoder_startjit(void *closure, const void *hd, size_t size_hint); +size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, + size_t size, const upb_bufhandle *handle); +bool upb_pbdecoder_end(void *closure, const void *handler_data); + +/* Decoder-internal functions that the JIT calls to handle fallback paths. */ +int32_t upb_pbdecoder_resume(upb_pbdecoder *d, void *p, const char *buf, + size_t size, const upb_bufhandle *handle); +size_t upb_pbdecoder_suspend(upb_pbdecoder *d); +int32_t upb_pbdecoder_skipunknown(upb_pbdecoder *d, int32_t fieldnum, + uint8_t wire_type); +int32_t upb_pbdecoder_checktag_slow(upb_pbdecoder *d, uint64_t expected); +int32_t upb_pbdecoder_decode_varint_slow(upb_pbdecoder *d, uint64_t *u64); +int32_t upb_pbdecoder_decode_f32(upb_pbdecoder *d, uint32_t *u32); +int32_t upb_pbdecoder_decode_f64(upb_pbdecoder *d, uint64_t *u64); +void upb_pbdecoder_seterr(upb_pbdecoder *d, const char *msg); + +/* Error messages that are shared between the bytecode and JIT decoders. */ +extern const char *kPbDecoderStackOverflow; +extern const char *kPbDecoderSubmessageTooLong; + +/* Access to decoderplan members needed by the decoder. */ +const char *upb_pbdecoder_getopname(unsigned int op); + +/* JIT codegen entry point. */ +void upb_pbdecoder_jit(mgroup *group); +void upb_pbdecoder_freejit(mgroup *group); +UPB_REFCOUNTED_CMETHODS(mgroup, mgroup_upcast) + +/* A special label that means "do field dispatch for this message and branch to + * wherever that takes you." */ +#define LABEL_DISPATCH 0 + +/* A special slot in the dispatch table that stores the epilogue (ENDMSG and/or + * RET) for branching to when we find an appropriate ENDGROUP tag. */ +#define DISPATCH_ENDMSG 0 + +/* It's important to use this invalid wire type instead of 0 (which is a valid + * wire type). */ +#define NO_WIRE_TYPE 0xff + +/* The dispatch table layout is: + * [field number] -> [ 48-bit offset ][ 8-bit wt2 ][ 8-bit wt1 ] + * + * If wt1 matches, jump to the 48-bit offset. If wt2 matches, lookup + * (UPB_MAX_FIELDNUMBER + fieldnum) and jump there. + * + * We need two wire types because of packed/non-packed compatibility. A + * primitive repeated field can use either wire type and be valid. While we + * could key the table on fieldnum+wiretype, the table would be 8x sparser. + * + * Storing two wire types in the primary value allows us to quickly rule out + * the second wire type without needing to do a separate lookup (this case is + * less common than an unknown field). */ +UPB_INLINE uint64_t upb_pbdecoder_packdispatch(uint64_t ofs, uint8_t wt1, + uint8_t wt2) { + return (ofs << 16) | (wt2 << 8) | wt1; +} + +UPB_INLINE void upb_pbdecoder_unpackdispatch(uint64_t dispatch, uint64_t *ofs, + uint8_t *wt1, uint8_t *wt2) { + *wt1 = (uint8_t)dispatch; + *wt2 = (uint8_t)(dispatch >> 8); + *ofs = dispatch >> 16; +} + +/* All of the functions in decoder.c that return int32_t return values according + * to the following scheme: + * 1. negative values indicate a return code from the following list. + * 2. positive values indicate that error or end of buffer was hit, and + * that the decode function should immediately return the given value + * (the decoder state has already been suspended and is ready to be + * resumed). */ +#define DECODE_OK -1 +#define DECODE_MISMATCH -2 /* Used only from checktag_slow(). */ +#define DECODE_ENDGROUP -3 /* Used only from checkunknown(). */ + +#define CHECK_RETURN(x) { int32_t ret = x; if (ret >= 0) return ret; } + +#endif /* UPB_DECODER_INT_H_ */ +/* +** A number of routines for varint manipulation (we keep them all around to +** have multiple approaches available for benchmarking). +*/ + +#ifndef UPB_VARINT_DECODER_H_ +#define UPB_VARINT_DECODER_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* A list of types as they are encoded on-the-wire. */ +typedef enum { + UPB_WIRE_TYPE_VARINT = 0, + UPB_WIRE_TYPE_64BIT = 1, + UPB_WIRE_TYPE_DELIMITED = 2, + UPB_WIRE_TYPE_START_GROUP = 3, + UPB_WIRE_TYPE_END_GROUP = 4, + UPB_WIRE_TYPE_32BIT = 5 +} upb_wiretype_t; + +#define UPB_MAX_WIRE_TYPE 5 + +/* The maximum number of bytes that it takes to encode a 64-bit varint. + * Note that with a better encoding this could be 9 (TODO: write up a + * wiki document about this). */ +#define UPB_PB_VARINT_MAX_LEN 10 + +/* Array of the "native" (ie. non-packed-repeated) wire type for the given a + * descriptor type (upb_descriptortype_t). */ +extern const uint8_t upb_pb_native_wire_types[]; + +/* Zig-zag encoding/decoding **************************************************/ + +UPB_INLINE int32_t upb_zzdec_32(uint32_t n) { + return (n >> 1) ^ -(int32_t)(n & 1); +} +UPB_INLINE int64_t upb_zzdec_64(uint64_t n) { + return (n >> 1) ^ -(int64_t)(n & 1); +} +UPB_INLINE uint32_t upb_zzenc_32(int32_t n) { return (n << 1) ^ (n >> 31); } +UPB_INLINE uint64_t upb_zzenc_64(int64_t n) { return (n << 1) ^ (n >> 63); } + +/* Decoding *******************************************************************/ + +/* All decoding functions return this struct by value. */ +typedef struct { + const char *p; /* NULL if the varint was unterminated. */ + uint64_t val; +} upb_decoderet; + +UPB_INLINE upb_decoderet upb_decoderet_make(const char *p, uint64_t val) { + upb_decoderet ret; + ret.p = p; + ret.val = val; + return ret; +} + +/* Four functions for decoding a varint of at most eight bytes. They are all + * functionally identical, but are implemented in different ways and likely have + * different performance profiles. We keep them around for performance testing. + * + * Note that these functions may not read byte-by-byte, so they must not be used + * unless there are at least eight bytes left in the buffer! */ +upb_decoderet upb_vdecode_max8_branch32(upb_decoderet r); +upb_decoderet upb_vdecode_max8_branch64(upb_decoderet r); +upb_decoderet upb_vdecode_max8_wright(upb_decoderet r); +upb_decoderet upb_vdecode_max8_massimino(upb_decoderet r); + +/* Template for a function that checks the first two bytes with branching + * and dispatches 2-10 bytes with a separate function. Note that this may read + * up to 10 bytes, so it must not be used unless there are at least ten bytes + * left in the buffer! */ +#define UPB_VARINT_DECODER_CHECK2(name, decode_max8_function) \ +UPB_INLINE upb_decoderet upb_vdecode_check2_ ## name(const char *_p) { \ + uint8_t *p = (uint8_t*)_p; \ + upb_decoderet r; \ + if ((*p & 0x80) == 0) { \ + /* Common case: one-byte varint. */ \ + return upb_decoderet_make(_p + 1, *p & 0x7fU); \ + } \ + r = upb_decoderet_make(_p + 2, (*p & 0x7fU) | ((*(p + 1) & 0x7fU) << 7)); \ + if ((*(p + 1) & 0x80) == 0) { \ + /* Two-byte varint. */ \ + return r; \ + } \ + /* Longer varint, fallback to out-of-line function. */ \ + return decode_max8_function(r); \ +} + +UPB_VARINT_DECODER_CHECK2(branch32, upb_vdecode_max8_branch32) +UPB_VARINT_DECODER_CHECK2(branch64, upb_vdecode_max8_branch64) +UPB_VARINT_DECODER_CHECK2(wright, upb_vdecode_max8_wright) +UPB_VARINT_DECODER_CHECK2(massimino, upb_vdecode_max8_massimino) +#undef UPB_VARINT_DECODER_CHECK2 + +/* Our canonical functions for decoding varints, based on the currently + * favored best-performing implementations. */ +UPB_INLINE upb_decoderet upb_vdecode_fast(const char *p) { + if (sizeof(long) == 8) + return upb_vdecode_check2_branch64(p); + else + return upb_vdecode_check2_branch32(p); +} + +UPB_INLINE upb_decoderet upb_vdecode_max8_fast(upb_decoderet r) { + return upb_vdecode_max8_massimino(r); +} + + +/* Encoding *******************************************************************/ + +UPB_INLINE int upb_value_size(uint64_t val) { +#ifdef __GNUC__ + int high_bit = 63 - __builtin_clzll(val); /* 0-based, undef if val == 0. */ +#else + int high_bit = 0; + uint64_t tmp = val; + while(tmp >>= 1) high_bit++; +#endif + return val == 0 ? 1 : high_bit / 8 + 1; +} + +/* Encodes a 64-bit varint into buf (which must be >=UPB_PB_VARINT_MAX_LEN + * bytes long), returning how many bytes were used. + * + * TODO: benchmark and optimize if necessary. */ +UPB_INLINE size_t upb_vencode64(uint64_t val, char *buf) { + size_t i; + if (val == 0) { buf[0] = 0; return 1; } + i = 0; + while (val) { + uint8_t byte = val & 0x7fU; + val >>= 7; + if (val) byte |= 0x80U; + buf[i++] = byte; + } + return i; +} + +UPB_INLINE size_t upb_varint_size(uint64_t val) { + char buf[UPB_PB_VARINT_MAX_LEN]; + return upb_vencode64(val, buf); +} + +/* Encodes a 32-bit varint, *not* sign-extended. */ +UPB_INLINE uint64_t upb_vencode32(uint32_t val) { + char buf[UPB_PB_VARINT_MAX_LEN]; + size_t bytes = upb_vencode64(val, buf); + uint64_t ret = 0; + assert(bytes <= 5); + memcpy(&ret, buf, bytes); + assert(ret <= 0xffffffffffU); + return ret; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* UPB_VARINT_DECODER_H_ */ +/* +** upb::pb::Encoder (upb_pb_encoder) +** +** Implements a set of upb_handlers that write protobuf data to the binary wire +** format. +** +** This encoder implementation does not have any access to any out-of-band or +** precomputed lengths for submessages, so it must buffer submessages internally +** before it can emit the first byte. +*/ + +#ifndef UPB_ENCODER_H_ +#define UPB_ENCODER_H_ + + +#ifdef __cplusplus +namespace upb { +namespace pb { +class Encoder; +} /* namespace pb */ +} /* namespace upb */ +#endif + +UPB_DECLARE_TYPE(upb::pb::Encoder, upb_pb_encoder) + +#define UPB_PBENCODER_MAX_NESTING 100 + +/* upb::pb::Encoder ***********************************************************/ + +/* Preallocation hint: decoder won't allocate more bytes than this when first + * constructed. This hint may be an overestimate for some build configurations. + * But if the decoder library is upgraded without recompiling the application, + * it may be an underestimate. */ +#define UPB_PB_ENCODER_SIZE 768 + +#ifdef __cplusplus + +class upb::pb::Encoder { + public: + /* Creates a new encoder in the given environment. The Handlers must have + * come from NewHandlers() below. */ + static Encoder* Create(Environment* env, const Handlers* handlers, + BytesSink* output); + + /* The input to the encoder. */ + Sink* input(); + + /* Creates a new set of handlers for this MessageDef. */ + static reffed_ptr NewHandlers(const MessageDef* msg); + + static const size_t kSize = UPB_PB_ENCODER_SIZE; + + private: + UPB_DISALLOW_POD_OPS(Encoder, upb::pb::Encoder) +}; + +#endif + +UPB_BEGIN_EXTERN_C + +const upb_handlers *upb_pb_encoder_newhandlers(const upb_msgdef *m, + const void *owner); +upb_sink *upb_pb_encoder_input(upb_pb_encoder *p); +upb_pb_encoder* upb_pb_encoder_create(upb_env* e, const upb_handlers* h, + upb_bytessink* output); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +namespace upb { +namespace pb { +inline Encoder* Encoder::Create(Environment* env, const Handlers* handlers, + BytesSink* output) { + return upb_pb_encoder_create(env, handlers, output); +} +inline Sink* Encoder::input() { + return upb_pb_encoder_input(this); +} +inline reffed_ptr Encoder::NewHandlers( + const upb::MessageDef *md) { + const Handlers* h = upb_pb_encoder_newhandlers(md, &h); + return reffed_ptr(h, &h); +} +} /* namespace pb */ +} /* namespace upb */ + +#endif + +#endif /* UPB_ENCODER_H_ */ +/* +** upb's core components like upb_decoder and upb_msg are carefully designed to +** avoid depending on each other for maximum orthogonality. In other words, +** you can use a upb_decoder to decode into *any* kind of structure; upb_msg is +** just one such structure. A upb_msg can be serialized/deserialized into any +** format, protobuf binary format is just one such format. +** +** However, for convenience we provide functions here for doing common +** operations like deserializing protobuf binary format into a upb_msg. The +** compromise is that this file drags in almost all of upb as a dependency, +** which could be undesirable if you're trying to use a trimmed-down build of +** upb. +** +** While these routines are convenient, they do not reuse any encoding/decoding +** state. For example, if a decoder is JIT-based, it will be re-JITted every +** time these functions are called. For this reason, if you are parsing lots +** of data and efficiency is an issue, these may not be the best functions to +** use (though they are useful for prototyping, before optimizing). +*/ + +#ifndef UPB_GLUE_H +#define UPB_GLUE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Loads all defs from the given protobuf binary descriptor, setting default + * accessors and a default layout on all messages. The caller owns the + * returned array of defs, which will be of length *n. On error NULL is + * returned and status is set (if non-NULL). */ +upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n, + void *owner, upb_status *status); + +/* Like the previous but also adds the loaded defs to the given symtab. */ +bool upb_load_descriptor_into_symtab(upb_symtab *symtab, const char *str, + size_t len, upb_status *status); + +/* Like the previous but also reads the descriptor from the given filename. */ +bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname, + upb_status *status); + +/* Reads the given filename into a character string, returning NULL if there + * was an error. */ +char *upb_readfile(const char *filename, size_t *len); + +#ifdef __cplusplus +} /* extern "C" */ + +namespace upb { + +/* All routines that load descriptors expect the descriptor to be a + * FileDescriptorSet. */ +inline bool LoadDescriptorFileIntoSymtab(SymbolTable* s, const char *fname, + Status* status) { + return upb_load_descriptor_file_into_symtab(s, fname, status); +} + +inline bool LoadDescriptorIntoSymtab(SymbolTable* s, const char* str, + size_t len, Status* status) { + return upb_load_descriptor_into_symtab(s, str, len, status); +} + +/* Templated so it can accept both string and std::string. */ +template +bool LoadDescriptorIntoSymtab(SymbolTable* s, const T& desc, Status* status) { + return upb_load_descriptor_into_symtab(s, desc.c_str(), desc.size(), status); +} + +} /* namespace upb */ + +#endif + +#endif /* UPB_GLUE_H */ +/* +** upb::pb::TextPrinter (upb_textprinter) +** +** Handlers for writing to protobuf text format. +*/ + +#ifndef UPB_TEXT_H_ +#define UPB_TEXT_H_ + + +#ifdef __cplusplus +namespace upb { +namespace pb { +class TextPrinter; +} /* namespace pb */ +} /* namespace upb */ +#endif + +UPB_DECLARE_TYPE(upb::pb::TextPrinter, upb_textprinter) + +#ifdef __cplusplus + +class upb::pb::TextPrinter { + public: + /* The given handlers must have come from NewHandlers(). It must outlive the + * TextPrinter. */ + static TextPrinter *Create(Environment *env, const upb::Handlers *handlers, + BytesSink *output); + + void SetSingleLineMode(bool single_line); + + Sink* input(); + + /* If handler caching becomes a requirement we can add a code cache as in + * decoder.h */ + static reffed_ptr NewHandlers(const MessageDef* md); +}; + +#endif + +UPB_BEGIN_EXTERN_C + +/* C API. */ +upb_textprinter *upb_textprinter_create(upb_env *env, const upb_handlers *h, + upb_bytessink *output); +void upb_textprinter_setsingleline(upb_textprinter *p, bool single_line); +upb_sink *upb_textprinter_input(upb_textprinter *p); + +const upb_handlers *upb_textprinter_newhandlers(const upb_msgdef *m, + const void *owner); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +namespace upb { +namespace pb { +inline TextPrinter *TextPrinter::Create(Environment *env, + const upb::Handlers *handlers, + BytesSink *output) { + return upb_textprinter_create(env, handlers, output); +} +inline void TextPrinter::SetSingleLineMode(bool single_line) { + upb_textprinter_setsingleline(this, single_line); +} +inline Sink* TextPrinter::input() { + return upb_textprinter_input(this); +} +inline reffed_ptr TextPrinter::NewHandlers( + const MessageDef *md) { + const Handlers* h = upb_textprinter_newhandlers(md, &h); + return reffed_ptr(h, &h); +} +} /* namespace pb */ +} /* namespace upb */ + +#endif + +#endif /* UPB_TEXT_H_ */ +/* +** upb::json::Parser (upb_json_parser) +** +** Parses JSON according to a specific schema. +** Support for parsing arbitrary JSON (schema-less) will be added later. +*/ + +#ifndef UPB_JSON_PARSER_H_ +#define UPB_JSON_PARSER_H_ + + +#ifdef __cplusplus +namespace upb { +namespace json { +class Parser; +} /* namespace json */ +} /* namespace upb */ +#endif + +UPB_DECLARE_TYPE(upb::json::Parser, upb_json_parser) + +/* upb::json::Parser **********************************************************/ + +/* Preallocation hint: parser won't allocate more bytes than this when first + * constructed. This hint may be an overestimate for some build configurations. + * But if the parser library is upgraded without recompiling the application, + * it may be an underestimate. */ +#define UPB_JSON_PARSER_SIZE 3704 + +#ifdef __cplusplus + +/* Parses an incoming BytesStream, pushing the results to the destination + * sink. */ +class upb::json::Parser { + public: + static Parser* Create(Environment* env, Sink* output); + + BytesSink* input(); + + private: + UPB_DISALLOW_POD_OPS(Parser, upb::json::Parser) +}; + +#endif + +UPB_BEGIN_EXTERN_C + +upb_json_parser *upb_json_parser_create(upb_env *e, upb_sink *output); +upb_bytessink *upb_json_parser_input(upb_json_parser *p); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +namespace upb { +namespace json { +inline Parser* Parser::Create(Environment* env, Sink* output) { + return upb_json_parser_create(env, output); +} +inline BytesSink* Parser::input() { + return upb_json_parser_input(this); +} +} /* namespace json */ +} /* namespace upb */ + +#endif + + +#endif /* UPB_JSON_PARSER_H_ */ +/* +** upb::json::Printer +** +** Handlers that emit JSON according to a specific protobuf schema. +*/ + +#ifndef UPB_JSON_TYPED_PRINTER_H_ +#define UPB_JSON_TYPED_PRINTER_H_ + + +#ifdef __cplusplus +namespace upb { +namespace json { +class Printer; +} /* namespace json */ +} /* namespace upb */ +#endif + +UPB_DECLARE_TYPE(upb::json::Printer, upb_json_printer) + + +/* upb::json::Printer *********************************************************/ + +#define UPB_JSON_PRINTER_SIZE 168 + +#ifdef __cplusplus + +/* Prints an incoming stream of data to a BytesSink in JSON format. */ +class upb::json::Printer { + public: + static Printer* Create(Environment* env, const upb::Handlers* handlers, + BytesSink* output); + + /* The input to the printer. */ + Sink* input(); + + /* Returns handlers for printing according to the specified schema. */ + static reffed_ptr NewHandlers(const upb::MessageDef* md); + + static const size_t kSize = UPB_JSON_PRINTER_SIZE; + + private: + UPB_DISALLOW_POD_OPS(Printer, upb::json::Printer) +}; + +#endif + +UPB_BEGIN_EXTERN_C + +/* Native C API. */ +upb_json_printer *upb_json_printer_create(upb_env *e, const upb_handlers *h, + upb_bytessink *output); +upb_sink *upb_json_printer_input(upb_json_printer *p); +const upb_handlers *upb_json_printer_newhandlers(const upb_msgdef *md, + const void *owner); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +namespace upb { +namespace json { +inline Printer* Printer::Create(Environment* env, const upb::Handlers* handlers, + BytesSink* output) { + return upb_json_printer_create(env, handlers, output); +} +inline Sink* Printer::input() { return upb_json_printer_input(this); } +inline reffed_ptr Printer::NewHandlers( + const upb::MessageDef *md) { + const Handlers* h = upb_json_printer_newhandlers(md, &h); + return reffed_ptr(h, &h); +} +} /* namespace json */ +} /* namespace upb */ + +#endif + +#endif /* UPB_JSON_TYPED_PRINTER_H_ */ diff --git a/php/tests/autoload.php b/php/tests/autoload.php new file mode 100644 index 000000000..af88ba014 --- /dev/null +++ b/php/tests/autoload.php @@ -0,0 +1,4 @@ +frame; +#ifndef PYPY_VERSION + // This check is not critical and is somewhat difficult to implement correctly + // in PyPy. + PyFrameObject* frame = PyEval_GetFrame(); if (frame == NULL) { return false; } @@ -130,6 +129,7 @@ bool _CalledFromGeneratedFile(int stacklevel) { // Filename is not ending with _pb2. return false; } +#endif return true; } @@ -200,8 +200,8 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) { // read-only instance. const Message& options(descriptor->options()); const Descriptor *message_type = options.GetDescriptor(); - PyObject* message_class(cdescriptor_pool::GetMessageClass( - pool, message_type)); + CMessageClass* message_class( + cdescriptor_pool::GetMessageClass(pool, message_type)); if (message_class == NULL) { // The Options message was not found in the current DescriptorPool. // In this case, there cannot be extensions to these options, and we can @@ -215,7 +215,8 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) { message_type->full_name().c_str()); return NULL; } - ScopedPyObjectPtr value(PyEval_CallObject(message_class, NULL)); + ScopedPyObjectPtr value( + PyEval_CallObject(message_class->AsPyObject(), NULL)); if (value == NULL) { return NULL; } @@ -433,11 +434,11 @@ static PyObject* GetConcreteClass(PyBaseDescriptor* self, void *closure) { // which contains this descriptor. // This might not be the one you expect! For example the returned object does // not know about extensions defined in a custom pool. - PyObject* concrete_class(cdescriptor_pool::GetMessageClass( + CMessageClass* concrete_class(cdescriptor_pool::GetMessageClass( GetDescriptorPool_FromPool(_GetDescriptor(self)->file()->pool()), _GetDescriptor(self))); Py_XINCREF(concrete_class); - return concrete_class; + return concrete_class->AsPyObject(); } static PyObject* GetFieldsByName(PyBaseDescriptor* self, void *closure) { diff --git a/python/google/protobuf/pyext/descriptor_database.cc b/python/google/protobuf/pyext/descriptor_database.cc index 514722b4c..daa40cc72 100644 --- a/python/google/protobuf/pyext/descriptor_database.cc +++ b/python/google/protobuf/pyext/descriptor_database.cc @@ -64,6 +64,9 @@ static bool GetFileDescriptorProto(PyObject* py_descriptor, } return false; } + if (py_descriptor == Py_None) { + return false; + } const Descriptor* filedescriptor_descriptor = FileDescriptorProto::default_instance().GetDescriptor(); CMessage* message = reinterpret_cast(py_descriptor); diff --git a/python/google/protobuf/pyext/descriptor_pool.cc b/python/google/protobuf/pyext/descriptor_pool.cc index 0bc76bc90..1faff96bc 100644 --- a/python/google/protobuf/pyext/descriptor_pool.cc +++ b/python/google/protobuf/pyext/descriptor_pool.cc @@ -190,8 +190,8 @@ PyObject* FindMessageByName(PyDescriptorPool* self, PyObject* arg) { // Add a message class to our database. int RegisterMessageClass(PyDescriptorPool* self, - const Descriptor *message_descriptor, - PyObject *message_class) { + const Descriptor* message_descriptor, + CMessageClass* message_class) { Py_INCREF(message_class); typedef PyDescriptorPool::ClassesByMessageMap::iterator iterator; std::pair ret = self->classes_by_descriptor->insert( @@ -205,8 +205,8 @@ int RegisterMessageClass(PyDescriptorPool* self, } // Retrieve the message class added to our database. -PyObject *GetMessageClass(PyDescriptorPool* self, - const Descriptor *message_descriptor) { +CMessageClass* GetMessageClass(PyDescriptorPool* self, + const Descriptor* message_descriptor) { typedef PyDescriptorPool::ClassesByMessageMap::iterator iterator; iterator ret = self->classes_by_descriptor->find(message_descriptor); if (ret == self->classes_by_descriptor->end()) { diff --git a/python/google/protobuf/pyext/descriptor_pool.h b/python/google/protobuf/pyext/descriptor_pool.h index 16bc910c4..2a42c1126 100644 --- a/python/google/protobuf/pyext/descriptor_pool.h +++ b/python/google/protobuf/pyext/descriptor_pool.h @@ -42,6 +42,9 @@ class MessageFactory; namespace python { +// The (meta) type of all Messages classes. +struct CMessageClass; + // Wraps operations to the global DescriptorPool which contains information // about all messages and fields. // @@ -78,7 +81,7 @@ typedef struct PyDescriptorPool { // // Descriptor pointers stored here are owned by the DescriptorPool above. // Python references to classes are owned by this PyDescriptorPool. - typedef hash_map ClassesByMessageMap; + typedef hash_map ClassesByMessageMap; ClassesByMessageMap* classes_by_descriptor; // Cache the options for any kind of descriptor. @@ -101,14 +104,14 @@ const Descriptor* FindMessageTypeByName(PyDescriptorPool* self, // On error, returns -1 with a Python exception set. int RegisterMessageClass(PyDescriptorPool* self, const Descriptor* message_descriptor, - PyObject* message_class); + CMessageClass* message_class); // Retrieves the Python class registered with the given message descriptor. // // Returns a *borrowed* reference if found, otherwise returns NULL with an // exception set. -PyObject* GetMessageClass(PyDescriptorPool* self, - const Descriptor* message_descriptor); +CMessageClass* GetMessageClass(PyDescriptorPool* self, + const Descriptor* message_descriptor); // The functions below are also exposed as methods of the DescriptorPool type. diff --git a/python/google/protobuf/pyext/extension_dict.cc b/python/google/protobuf/pyext/extension_dict.cc index 555bd2937..21bbb8c2b 100644 --- a/python/google/protobuf/pyext/extension_dict.cc +++ b/python/google/protobuf/pyext/extension_dict.cc @@ -130,7 +130,7 @@ PyObject* subscript(ExtensionDict* self, PyObject* key) { if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) { if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - PyObject *message_class = cdescriptor_pool::GetMessageClass( + CMessageClass* message_class = cdescriptor_pool::GetMessageClass( cmessage::GetDescriptorPoolForMessage(self->parent), descriptor->message_type()); if (message_class == NULL) { @@ -239,6 +239,21 @@ PyObject* _FindExtensionByName(ExtensionDict* self, PyObject* name) { } } +PyObject* _FindExtensionByNumber(ExtensionDict* self, PyObject* number) { + ScopedPyObjectPtr extensions_by_number(PyObject_GetAttrString( + reinterpret_cast(self->parent), "_extensions_by_number")); + if (extensions_by_number == NULL) { + return NULL; + } + PyObject* result = PyDict_GetItem(extensions_by_number.get(), number); + if (result == NULL) { + Py_RETURN_NONE; + } else { + Py_INCREF(result); + return result; + } +} + ExtensionDict* NewExtensionDict(CMessage *parent) { ExtensionDict* self = reinterpret_cast( PyType_GenericAlloc(&ExtensionDict_Type, 0)); @@ -271,6 +286,8 @@ static PyMethodDef Methods[] = { EDMETHOD(HasExtension, METH_O, "Checks if the object has an extension."), EDMETHOD(_FindExtensionByName, METH_O, "Finds an extension by name."), + EDMETHOD(_FindExtensionByNumber, METH_O, + "Finds an extension by field number."), { NULL, NULL } }; diff --git a/python/google/protobuf/pyext/extension_dict.h b/python/google/protobuf/pyext/extension_dict.h index d92cf956b..2456eda1e 100644 --- a/python/google/protobuf/pyext/extension_dict.h +++ b/python/google/protobuf/pyext/extension_dict.h @@ -123,6 +123,12 @@ PyObject* ClearExtension(ExtensionDict* self, // Returns a new reference. PyObject* _FindExtensionByName(ExtensionDict* self, PyObject* name); +// Gets an extension from the dict given the extension field number as +// opposed to descriptor. +// +// Returns a new reference. +PyObject* _FindExtensionByNumber(ExtensionDict* self, PyObject* number); + } // namespace extension_dict } // namespace python } // namespace protobuf diff --git a/python/google/protobuf/pyext/map_container.cc b/python/google/protobuf/pyext/map_container.cc index df9138a4d..e022406d1 100644 --- a/python/google/protobuf/pyext/map_container.cc +++ b/python/google/protobuf/pyext/map_container.cc @@ -32,6 +32,11 @@ #include +#include +#ifndef _SHARED_PTR_H +#include +#endif + #include #include #include @@ -70,7 +75,7 @@ class MapReflectionFriend { struct MapIterator { PyObject_HEAD; - scoped_ptr< ::google::protobuf::MapIterator> iter; + google::protobuf::scoped_ptr< ::google::protobuf::MapIterator> iter; // A pointer back to the container, so we can notice changes to the version. // We own a ref on this. @@ -610,8 +615,7 @@ static PyObject* GetCMessage(MessageMapContainer* self, Message* message) { PyObject* ret = PyDict_GetItem(self->message_dict, key.get()); if (ret == NULL) { - CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init, - message->GetDescriptor()); + CMessage* cmsg = cmessage::NewEmptyMessage(self->message_class); ret = reinterpret_cast(cmsg); if (cmsg == NULL) { @@ -634,7 +638,7 @@ static PyObject* GetCMessage(MessageMapContainer* self, Message* message) { PyObject* NewMessageMapContainer( CMessage* parent, const google::protobuf::FieldDescriptor* parent_field_descriptor, - PyObject* concrete_class) { + CMessageClass* message_class) { if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) { return NULL; } @@ -669,8 +673,8 @@ PyObject* NewMessageMapContainer( "Could not allocate message dict."); } - Py_INCREF(concrete_class); - self->subclass_init = concrete_class; + Py_INCREF(message_class); + self->message_class = message_class; if (self->key_field_descriptor == NULL || self->value_field_descriptor == NULL) { @@ -763,6 +767,7 @@ static void MessageMapDealloc(PyObject* _self) { MessageMapContainer* self = GetMessageMap(_self); self->owner.reset(); Py_DECREF(self->message_dict); + Py_DECREF(self->message_class); Py_TYPE(_self)->tp_free(_self); } diff --git a/python/google/protobuf/pyext/map_container.h b/python/google/protobuf/pyext/map_container.h index ddf94be71..fbd6713f7 100644 --- a/python/google/protobuf/pyext/map_container.h +++ b/python/google/protobuf/pyext/map_container.h @@ -55,6 +55,7 @@ using internal::shared_ptr; namespace python { struct CMessage; +struct CMessageClass; // This struct is used directly for ScalarMap, and is the base class of // MessageMapContainer, which is used for MessageMap. @@ -104,8 +105,8 @@ struct MapContainer { }; struct MessageMapContainer : public MapContainer { - // A callable that is used to create new child messages. - PyObject* subclass_init; + // The type used to create new child messages. + CMessageClass* message_class; // A dict mapping Message* -> CMessage. PyObject* message_dict; @@ -132,7 +133,7 @@ extern PyObject* NewScalarMapContainer( // field descriptor. extern PyObject* NewMessageMapContainer( CMessage* parent, const FieldDescriptor* parent_field_descriptor, - PyObject* concrete_class); + CMessageClass* message_class); } // namespace python } // namespace protobuf diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc index 60ec9c1b9..83c151ff6 100644 --- a/python/google/protobuf/pyext/message.cc +++ b/python/google/protobuf/pyext/message.cc @@ -98,31 +98,6 @@ static PyObject* PythonMessage_class; static PyObject* kEmptyWeakref; static PyObject* WKT_classes = NULL; -// Defines the Metaclass of all Message classes. -// It allows us to cache some C++ pointers in the class object itself, they are -// faster to extract than from the type's dictionary. - -struct PyMessageMeta { - // This is how CPython subclasses C structures: the base structure must be - // the first member of the object. - PyHeapTypeObject super; - - // C++ descriptor of this message. - const Descriptor* message_descriptor; - - // Owned reference, used to keep the pointer above alive. - PyObject* py_message_descriptor; - - // The Python DescriptorPool used to create the class. It is needed to resolve - // fields descriptors, including extensions fields; its C++ MessageFactory is - // used to instantiate submessages. - // This can be different from DESCRIPTOR.file.pool, in the case of a custom - // DescriptorPool which defines new extensions. - // We own the reference, because it's important to keep the descriptors and - // factory alive. - PyDescriptorPool* py_descriptor_pool; -}; - namespace message_meta { static int InsertEmptyWeakref(PyTypeObject* base); @@ -173,10 +148,6 @@ static int AddDescriptors(PyObject* cls, const Descriptor* descriptor) { } // For each enum set cls. = EnumTypeWrapper(). - // - // The enum descriptor we get from - // .enum_types_by_name[name] - // which was built previously. for (int i = 0; i < descriptor->enum_type_count(); ++i) { const EnumDescriptor* enum_descriptor = descriptor->enum_type(i); ScopedPyObjectPtr enum_type( @@ -309,7 +280,7 @@ static PyObject* New(PyTypeObject* type, if (result == NULL) { return NULL; } - PyMessageMeta* newtype = reinterpret_cast(result.get()); + CMessageClass* newtype = reinterpret_cast(result.get()); // Insert the empty weakref into the base classes. if (InsertEmptyWeakref( @@ -338,7 +309,7 @@ static PyObject* New(PyTypeObject* type, // Add the message to the DescriptorPool. if (cdescriptor_pool::RegisterMessageClass(newtype->py_descriptor_pool, - descriptor, result.get()) < 0) { + descriptor, newtype) < 0) { return NULL; } @@ -349,7 +320,7 @@ static PyObject* New(PyTypeObject* type, return result.release(); } -static void Dealloc(PyMessageMeta *self) { +static void Dealloc(CMessageClass *self) { Py_DECREF(self->py_message_descriptor); Py_DECREF(self->py_descriptor_pool); Py_TYPE(self)->tp_free(reinterpret_cast(self)); @@ -378,10 +349,10 @@ static int InsertEmptyWeakref(PyTypeObject *base_type) { } // namespace message_meta -PyTypeObject PyMessageMeta_Type = { +PyTypeObject CMessageClass_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME ".MessageMeta", // tp_name - sizeof(PyMessageMeta), // tp_basicsize + sizeof(CMessageClass), // tp_basicsize 0, // tp_itemsize (destructor)message_meta::Dealloc, // tp_dealloc 0, // tp_print @@ -419,16 +390,16 @@ PyTypeObject PyMessageMeta_Type = { message_meta::New, // tp_new }; -static PyMessageMeta* CheckMessageClass(PyTypeObject* cls) { - if (!PyObject_TypeCheck(cls, &PyMessageMeta_Type)) { +static CMessageClass* CheckMessageClass(PyTypeObject* cls) { + if (!PyObject_TypeCheck(cls, &CMessageClass_Type)) { PyErr_Format(PyExc_TypeError, "Class %s is not a Message", cls->tp_name); return NULL; } - return reinterpret_cast(cls); + return reinterpret_cast(cls); } static const Descriptor* GetMessageDescriptor(PyTypeObject* cls) { - PyMessageMeta* type = CheckMessageClass(cls); + CMessageClass* type = CheckMessageClass(cls); if (type == NULL) { return NULL; } @@ -783,9 +754,9 @@ namespace cmessage { PyDescriptorPool* GetDescriptorPoolForMessage(CMessage* message) { // No need to check the type: the type of instances of CMessage is always - // an instance of PyMessageMeta. Let's prove it with a debug-only check. + // an instance of CMessageClass. Let's prove it with a debug-only check. GOOGLE_DCHECK(PyObject_TypeCheck(message, &CMessage_Type)); - return reinterpret_cast(Py_TYPE(message))->py_descriptor_pool; + return reinterpret_cast(Py_TYPE(message))->py_descriptor_pool; } MessageFactory* GetFactoryForMessage(CMessage* message) { @@ -1090,6 +1061,10 @@ int InitAttributes(CMessage* self, PyObject* kwargs) { PyString_AsString(name)); return -1; } + if (value == Py_None) { + // field=None is the same as no field at all. + continue; + } if (descriptor->is_map()) { ScopedPyObjectPtr map(GetAttr(self, name)); const FieldDescriptor* value_descriptor = @@ -1220,9 +1195,9 @@ int InitAttributes(CMessage* self, PyObject* kwargs) { // Allocates an incomplete Python Message: the caller must fill self->message, // self->owner and eventually self->parent. -CMessage* NewEmptyMessage(PyObject* type, const Descriptor *descriptor) { +CMessage* NewEmptyMessage(CMessageClass* type) { CMessage* self = reinterpret_cast( - PyType_GenericAlloc(reinterpret_cast(type), 0)); + PyType_GenericAlloc(&type->super.ht_type, 0)); if (self == NULL) { return NULL; } @@ -1242,7 +1217,7 @@ CMessage* NewEmptyMessage(PyObject* type, const Descriptor *descriptor) { // Creates a new C++ message and takes ownership. static PyObject* New(PyTypeObject* cls, PyObject* unused_args, PyObject* unused_kwargs) { - PyMessageMeta* type = CheckMessageClass(cls); + CMessageClass* type = CheckMessageClass(cls); if (type == NULL) { return NULL; } @@ -1258,8 +1233,7 @@ static PyObject* New(PyTypeObject* cls, return NULL; } - CMessage* self = NewEmptyMessage(reinterpret_cast(type), - message_descriptor); + CMessage* self = NewEmptyMessage(type); if (self == NULL) { return NULL; } @@ -1851,8 +1825,12 @@ static PyObject* ToStr(CMessage* self) { PyObject* MergeFrom(CMessage* self, PyObject* arg) { CMessage* other_message; - if (!PyObject_TypeCheck(reinterpret_cast(arg), &CMessage_Type)) { - PyErr_SetString(PyExc_TypeError, "Must be a message"); + if (!PyObject_TypeCheck(arg, &CMessage_Type)) { + PyErr_Format(PyExc_TypeError, + "Parameter to MergeFrom() must be instance of same class: " + "expected %s got %s.", + self->message->GetDescriptor()->full_name().c_str(), + Py_TYPE(arg)->tp_name); return NULL; } @@ -1860,8 +1838,8 @@ PyObject* MergeFrom(CMessage* self, PyObject* arg) { if (other_message->message->GetDescriptor() != self->message->GetDescriptor()) { PyErr_Format(PyExc_TypeError, - "Tried to merge from a message with a different type. " - "to: %s, from: %s", + "Parameter to MergeFrom() must be instance of same class: " + "expected %s got %s.", self->message->GetDescriptor()->full_name().c_str(), other_message->message->GetDescriptor()->full_name().c_str()); return NULL; @@ -1879,8 +1857,12 @@ PyObject* MergeFrom(CMessage* self, PyObject* arg) { static PyObject* CopyFrom(CMessage* self, PyObject* arg) { CMessage* other_message; - if (!PyObject_TypeCheck(reinterpret_cast(arg), &CMessage_Type)) { - PyErr_SetString(PyExc_TypeError, "Must be a message"); + if (!PyObject_TypeCheck(arg, &CMessage_Type)) { + PyErr_Format(PyExc_TypeError, + "Parameter to CopyFrom() must be instance of same class: " + "expected %s got %s.", + self->message->GetDescriptor()->full_name().c_str(), + Py_TYPE(arg)->tp_name); return NULL; } @@ -1893,8 +1875,8 @@ static PyObject* CopyFrom(CMessage* self, PyObject* arg) { if (other_message->message->GetDescriptor() != self->message->GetDescriptor()) { PyErr_Format(PyExc_TypeError, - "Tried to copy from a message with a different type. " - "to: %s, from: %s", + "Parameter to CopyFrom() must be instance of same class: " + "expected %s got %s.", self->message->GetDescriptor()->full_name().c_str(), other_message->message->GetDescriptor()->full_name().c_str()); return NULL; @@ -1911,6 +1893,30 @@ static PyObject* CopyFrom(CMessage* self, PyObject* arg) { Py_RETURN_NONE; } +// Protobuf has a 64MB limit built in, this variable will override this. Please +// do not enable this unless you fully understand the implications: protobufs +// must all be kept in memory at the same time, so if they grow too big you may +// get OOM errors. The protobuf APIs do not provide any tools for processing +// protobufs in chunks. If you have protos this big you should break them up if +// it is at all convenient to do so. +static bool allow_oversize_protos = false; + +// Provide a method in the module to set allow_oversize_protos to a boolean +// value. This method returns the newly value of allow_oversize_protos. +static PyObject* SetAllowOversizeProtos(PyObject* m, PyObject* arg) { + if (!arg || !PyBool_Check(arg)) { + PyErr_SetString(PyExc_TypeError, + "Argument to SetAllowOversizeProtos must be boolean"); + return NULL; + } + allow_oversize_protos = PyObject_IsTrue(arg); + if (allow_oversize_protos) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + static PyObject* MergeFromString(CMessage* self, PyObject* arg) { const void* data; Py_ssize_t data_length; @@ -1921,15 +1927,9 @@ static PyObject* MergeFromString(CMessage* self, PyObject* arg) { AssureWritable(self); io::CodedInputStream input( reinterpret_cast(data), data_length); -#if PROTOBUF_PYTHON_ALLOW_OVERSIZE_PROTOS - // Protobuf has a 64MB limit built in, this code will override this. Please do - // not enable this unless you fully understand the implications: protobufs - // must all be kept in memory at the same time, so if they grow too big you - // may get OOM errors. The protobuf APIs do not provide any tools for - // processing protobufs in chunks. If you have protos this big you should - // break them up if it is at all convenient to do so. - input.SetTotalBytesLimit(INT_MAX, INT_MAX); -#endif // PROTOBUF_PYTHON_ALLOW_OVERSIZE_PROTOS + if (allow_oversize_protos) { + input.SetTotalBytesLimit(INT_MAX, INT_MAX); + } PyDescriptorPool* pool = GetDescriptorPoolForMessage(self); input.SetExtensionRegistry(pool->pool, pool->message_factory); bool success = self->message->MergePartialFromCodedStream(&input); @@ -1997,10 +1997,34 @@ static PyObject* RegisterExtension(PyObject* cls, PyErr_SetString(PyExc_TypeError, "no extensions_by_number on class"); return NULL; } + ScopedPyObjectPtr number(PyObject_GetAttrString(extension_handle, "number")); if (number == NULL) { return NULL; } + + // If the extension was already registered by number, check that it is the + // same. + existing_extension = PyDict_GetItem(extensions_by_number.get(), number.get()); + if (existing_extension != NULL) { + const FieldDescriptor* existing_extension_descriptor = + GetExtensionDescriptor(existing_extension); + if (existing_extension_descriptor != descriptor) { + const Descriptor* msg_desc = GetMessageDescriptor( + reinterpret_cast(cls)); + PyErr_Format( + PyExc_ValueError, + "Extensions \"%s\" and \"%s\" both try to extend message type " + "\"%s\" with field number %ld.", + existing_extension_descriptor->full_name().c_str(), + descriptor->full_name().c_str(), + msg_desc->full_name().c_str(), + PyInt_AsLong(number.get())); + return NULL; + } + // Nothing else to do. + Py_RETURN_NONE; + } if (PyDict_SetItem(extensions_by_number.get(), number.get(), extension_handle) < 0) { return NULL; @@ -2132,10 +2156,20 @@ static PyObject* ListFields(CMessage* self) { PyList_SET_ITEM(all_fields.get(), actual_size, t.release()); ++actual_size; } - Py_SIZE(all_fields.get()) = actual_size; + if (static_cast(actual_size) != fields.size() && + (PyList_SetSlice(all_fields.get(), actual_size, fields.size(), NULL) < + 0)) { + return NULL; + } return all_fields.release(); } +static PyObject* DiscardUnknownFields(CMessage* self) { + AssureWritable(self); + self->message->DiscardUnknownFields(); + Py_RETURN_NONE; +} + PyObject* FindInitializationErrors(CMessage* self) { Message* message = self->message; vector errors; @@ -2279,14 +2313,13 @@ PyObject* InternalGetSubMessage( const Message& sub_message = reflection->GetMessage( *self->message, field_descriptor, pool->message_factory); - PyObject *message_class = cdescriptor_pool::GetMessageClass( + CMessageClass* message_class = cdescriptor_pool::GetMessageClass( pool, field_descriptor->message_type()); if (message_class == NULL) { return NULL; } - CMessage* cmsg = cmessage::NewEmptyMessage(message_class, - sub_message.GetDescriptor()); + CMessage* cmsg = cmessage::NewEmptyMessage(message_class); if (cmsg == NULL) { return NULL; } @@ -2555,6 +2588,8 @@ static PyMethodDef Methods[] = { "Clears a message field." }, { "CopyFrom", (PyCFunction)CopyFrom, METH_O, "Copies a protocol message into the current message." }, + { "DiscardUnknownFields", (PyCFunction)DiscardUnknownFields, METH_NOARGS, + "Discards the unknown fields." }, { "FindInitializationErrors", (PyCFunction)FindInitializationErrors, METH_NOARGS, "Finds unset required fields." }, @@ -2624,7 +2659,7 @@ PyObject* GetAttr(CMessage* self, PyObject* name) { const Descriptor* entry_type = field_descriptor->message_type(); const FieldDescriptor* value_type = entry_type->FindFieldByName("value"); if (value_type->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - PyObject* value_class = cdescriptor_pool::GetMessageClass( + CMessageClass* value_class = cdescriptor_pool::GetMessageClass( GetDescriptorPoolForMessage(self), value_type->message_type()); if (value_class == NULL) { return NULL; @@ -2647,7 +2682,7 @@ PyObject* GetAttr(CMessage* self, PyObject* name) { if (field_descriptor->label() == FieldDescriptor::LABEL_REPEATED) { PyObject* py_container = NULL; if (field_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - PyObject *message_class = cdescriptor_pool::GetMessageClass( + CMessageClass* message_class = cdescriptor_pool::GetMessageClass( GetDescriptorPoolForMessage(self), field_descriptor->message_type()); if (message_class == NULL) { return NULL; @@ -2711,7 +2746,7 @@ int SetAttr(CMessage* self, PyObject* name, PyObject* value) { PyErr_Format(PyExc_AttributeError, "Assignment not allowed " - "(no field \"%s\"in protocol message object).", + "(no field \"%s\" in protocol message object).", PyString_AsString(name)); return -1; } @@ -2719,7 +2754,7 @@ int SetAttr(CMessage* self, PyObject* name, PyObject* value) { } // namespace cmessage PyTypeObject CMessage_Type = { - PyVarObject_HEAD_INIT(&PyMessageMeta_Type, 0) + PyVarObject_HEAD_INIT(&CMessageClass_Type, 0) FULL_MODULE_NAME ".CMessage", // tp_name sizeof(CMessage), // tp_basicsize 0, // tp_itemsize @@ -2728,7 +2763,7 @@ PyTypeObject CMessage_Type = { 0, // tp_getattr 0, // tp_setattr 0, // tp_compare - 0, // tp_repr + (reprfunc)cmessage::ToStr, // tp_repr 0, // tp_as_number 0, // tp_as_sequence 0, // tp_as_mapping @@ -2834,12 +2869,12 @@ bool InitProto2MessageModule(PyObject *m) { // Initialize constants defined in this file. InitGlobals(); - PyMessageMeta_Type.tp_base = &PyType_Type; - if (PyType_Ready(&PyMessageMeta_Type) < 0) { + CMessageClass_Type.tp_base = &PyType_Type; + if (PyType_Ready(&CMessageClass_Type) < 0) { return false; } PyModule_AddObject(m, "MessageMeta", - reinterpret_cast(&PyMessageMeta_Type)); + reinterpret_cast(&CMessageClass_Type)); if (PyType_Ready(&CMessage_Type) < 0) { return false; @@ -3046,6 +3081,12 @@ bool InitProto2MessageModule(PyObject *m) { } // namespace python } // namespace protobuf +static PyMethodDef ModuleMethods[] = { + {"SetAllowOversizeProtos", + (PyCFunction)google::protobuf::python::cmessage::SetAllowOversizeProtos, + METH_O, "Enable/disable oversize proto parsing."}, + { NULL, NULL} +}; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef _module = { @@ -3053,7 +3094,7 @@ static struct PyModuleDef _module = { "_message", google::protobuf::python::module_docstring, -1, - NULL, + ModuleMethods, /* m_methods */ NULL, NULL, NULL, @@ -3072,7 +3113,8 @@ extern "C" { #if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&_module); #else - m = Py_InitModule3("_message", NULL, google::protobuf::python::module_docstring); + m = Py_InitModule3("_message", ModuleMethods, + google::protobuf::python::module_docstring); #endif if (m == NULL) { return INITFUNC_ERRORVAL; diff --git a/python/google/protobuf/pyext/message.h b/python/google/protobuf/pyext/message.h index cc0012e95..3a4bec81c 100644 --- a/python/google/protobuf/pyext/message.h +++ b/python/google/protobuf/pyext/message.h @@ -54,7 +54,7 @@ class MessageFactory; #ifdef _SHARED_PTR_H using std::shared_ptr; -using std::string; +using ::std::string; #else using internal::shared_ptr; #endif @@ -116,12 +116,43 @@ typedef struct CMessage { extern PyTypeObject CMessage_Type; + +// The (meta) type of all Messages classes. +// It allows us to cache some C++ pointers in the class object itself, they are +// faster to extract than from the type's dictionary. + +struct CMessageClass { + // This is how CPython subclasses C structures: the base structure must be + // the first member of the object. + PyHeapTypeObject super; + + // C++ descriptor of this message. + const Descriptor* message_descriptor; + + // Owned reference, used to keep the pointer above alive. + PyObject* py_message_descriptor; + + // The Python DescriptorPool used to create the class. It is needed to resolve + // fields descriptors, including extensions fields; its C++ MessageFactory is + // used to instantiate submessages. + // This can be different from DESCRIPTOR.file.pool, in the case of a custom + // DescriptorPool which defines new extensions. + // We own the reference, because it's important to keep the descriptors and + // factory alive. + PyDescriptorPool* py_descriptor_pool; + + PyObject* AsPyObject() { + return reinterpret_cast(this); + } +}; + + namespace cmessage { // Internal function to create a new empty Message Python object, but with empty // pointers to the C++ objects. // The caller must fill self->message, self->owner and eventually self->parent. -CMessage* NewEmptyMessage(PyObject* type, const Descriptor* descriptor); +CMessage* NewEmptyMessage(CMessageClass* type); // Release a submessage from its proto tree, making it a new top-level messgae. // A new message will be created if this is a read-only default instance. diff --git a/python/google/protobuf/pyext/repeated_composite_container.cc b/python/google/protobuf/pyext/repeated_composite_container.cc index b01123b4c..4f339e772 100644 --- a/python/google/protobuf/pyext/repeated_composite_container.cc +++ b/python/google/protobuf/pyext/repeated_composite_container.cc @@ -107,8 +107,7 @@ static int UpdateChildMessages(RepeatedCompositeContainer* self) { for (Py_ssize_t i = child_length; i < message_length; ++i) { const Message& sub_message = reflection->GetRepeatedMessage( *(self->message), self->parent_field_descriptor, i); - CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init, - sub_message.GetDescriptor()); + CMessage* cmsg = cmessage::NewEmptyMessage(self->child_message_class); ScopedPyObjectPtr py_cmsg(reinterpret_cast(cmsg)); if (cmsg == NULL) { return -1; @@ -140,8 +139,7 @@ static PyObject* AddToAttached(RepeatedCompositeContainer* self, Message* sub_message = message->GetReflection()->AddMessage(message, self->parent_field_descriptor); - CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init, - sub_message->GetDescriptor()); + CMessage* cmsg = cmessage::NewEmptyMessage(self->child_message_class); if (cmsg == NULL) return NULL; @@ -168,7 +166,7 @@ static PyObject* AddToReleased(RepeatedCompositeContainer* self, // Create a new Message detached from the rest. PyObject* py_cmsg = PyEval_CallObjectWithKeywords( - self->subclass_init, NULL, kwargs); + self->child_message_class->AsPyObject(), NULL, kwargs); if (py_cmsg == NULL) return NULL; @@ -506,7 +504,7 @@ int SetOwner(RepeatedCompositeContainer* self, PyObject *NewContainer( CMessage* parent, const FieldDescriptor* parent_field_descriptor, - PyObject *concrete_class) { + CMessageClass* concrete_class) { if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) { return NULL; } @@ -523,7 +521,7 @@ PyObject *NewContainer( self->parent_field_descriptor = parent_field_descriptor; self->owner = parent->owner; Py_INCREF(concrete_class); - self->subclass_init = concrete_class; + self->child_message_class = concrete_class; self->child_messages = PyList_New(0); return reinterpret_cast(self); @@ -531,7 +529,7 @@ PyObject *NewContainer( static void Dealloc(RepeatedCompositeContainer* self) { Py_CLEAR(self->child_messages); - Py_CLEAR(self->subclass_init); + Py_CLEAR(self->child_message_class); // TODO(tibell): Do we need to call delete on these objects to make // sure their destructors are called? self->owner.reset(); diff --git a/python/google/protobuf/pyext/repeated_composite_container.h b/python/google/protobuf/pyext/repeated_composite_container.h index 58d37b020..a7b56b61b 100644 --- a/python/google/protobuf/pyext/repeated_composite_container.h +++ b/python/google/protobuf/pyext/repeated_composite_container.h @@ -58,6 +58,7 @@ using internal::shared_ptr; namespace python { struct CMessage; +struct CMessageClass; // A RepeatedCompositeContainer can be in one of two states: attached // or released. @@ -94,8 +95,8 @@ typedef struct RepeatedCompositeContainer { // calling Clear() or ClearField() on the parent. Message* message; - // A callable that is used to create new child messages. - PyObject* subclass_init; + // The type used to create new child messages. + CMessageClass* child_message_class; // A list of child messages. PyObject* child_messages; @@ -110,7 +111,7 @@ namespace repeated_composite_container { PyObject *NewContainer( CMessage* parent, const FieldDescriptor* parent_field_descriptor, - PyObject *concrete_class); + CMessageClass *child_message_class); // Appends a new CMessage to the container and returns it. The // CMessage is initialized using the content of kwargs. diff --git a/python/google/protobuf/text_format.py b/python/google/protobuf/text_format.py index 8d256076c..6f1e3c8b7 100755 --- a/python/google/protobuf/text_format.py +++ b/python/google/protobuf/text_format.py @@ -99,7 +99,7 @@ class TextWriter(object): def MessageToString(message, as_utf8=False, as_one_line=False, pointy_brackets=False, use_index_order=False, - float_format=None): + float_format=None, use_field_number=False): """Convert protobuf message to text format. Floating point values can be formatted compactly with 15 digits of @@ -118,15 +118,16 @@ def MessageToString(message, as_utf8=False, as_one_line=False, field number order. float_format: If set, use this to specify floating point number formatting (per the "Format Specification Mini-Language"); otherwise, str() is used. + use_field_number: If True, print field numbers instead of names. Returns: A string of the text formatted protocol buffer message. """ out = TextWriter(as_utf8) - PrintMessage(message, out, as_utf8=as_utf8, as_one_line=as_one_line, - pointy_brackets=pointy_brackets, - use_index_order=use_index_order, - float_format=float_format) + printer = _Printer(out, 0, as_utf8, as_one_line, + pointy_brackets, use_index_order, float_format, + use_field_number) + printer.PrintMessage(message) result = out.getvalue() out.close() if as_one_line: @@ -142,133 +143,187 @@ def _IsMapEntry(field): def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False, pointy_brackets=False, use_index_order=False, - float_format=None): - fields = message.ListFields() - if use_index_order: - fields.sort(key=lambda x: x[0].index) - for field, value in fields: - if _IsMapEntry(field): - for key in sorted(value): - # This is slow for maps with submessage entires because it copies the - # entire tree. Unfortunately this would take significant refactoring - # of this file to work around. - # - # TODO(haberman): refactor and optimize if this becomes an issue. - entry_submsg = field.message_type._concrete_class( - key=key, value=value[key]) - PrintField(field, entry_submsg, out, indent, as_utf8, as_one_line, - pointy_brackets=pointy_brackets, - use_index_order=use_index_order, float_format=float_format) - elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: - for element in value: - PrintField(field, element, out, indent, as_utf8, as_one_line, - pointy_brackets=pointy_brackets, - use_index_order=use_index_order, - float_format=float_format) - else: - PrintField(field, value, out, indent, as_utf8, as_one_line, - pointy_brackets=pointy_brackets, - use_index_order=use_index_order, - float_format=float_format) + float_format=None, use_field_number=False): + printer = _Printer(out, indent, as_utf8, as_one_line, + pointy_brackets, use_index_order, float_format, + use_field_number) + printer.PrintMessage(message) def PrintField(field, value, out, indent=0, as_utf8=False, as_one_line=False, pointy_brackets=False, use_index_order=False, float_format=None): - """Print a single field name/value pair. For repeated fields, the value - should be a single element. - """ - - out.write(' ' * indent) - if field.is_extension: - out.write('[') - if (field.containing_type.GetOptions().message_set_wire_format and - field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and - field.label == descriptor.FieldDescriptor.LABEL_OPTIONAL): - out.write(field.message_type.full_name) - else: - out.write(field.full_name) - out.write(']') - elif field.type == descriptor.FieldDescriptor.TYPE_GROUP: - # For groups, use the capitalized name. - out.write(field.message_type.name) - else: - out.write(field.name) - - if field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_MESSAGE: - # The colon is optional in this case, but our cross-language golden files - # don't include it. - out.write(': ') - - PrintFieldValue(field, value, out, indent, as_utf8, as_one_line, - pointy_brackets=pointy_brackets, - use_index_order=use_index_order, - float_format=float_format) - if as_one_line: - out.write(' ') - else: - out.write('\n') + """Print a single field name/value pair.""" + printer = _Printer(out, indent, as_utf8, as_one_line, + pointy_brackets, use_index_order, float_format) + printer.PrintField(field, value) def PrintFieldValue(field, value, out, indent=0, as_utf8=False, as_one_line=False, pointy_brackets=False, use_index_order=False, float_format=None): - """Print a single field value (not including name). For repeated fields, - the value should be a single element.""" + """Print a single field value (not including name).""" + printer = _Printer(out, indent, as_utf8, as_one_line, + pointy_brackets, use_index_order, float_format) + printer.PrintFieldValue(field, value) - if pointy_brackets: - openb = '<' - closeb = '>' - else: - openb = '{' - closeb = '}' - if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: - if as_one_line: - out.write(' %s ' % openb) - PrintMessage(value, out, indent, as_utf8, as_one_line, - pointy_brackets=pointy_brackets, - use_index_order=use_index_order, - float_format=float_format) - out.write(closeb) +class _Printer(object): + """Text format printer for protocol message.""" + + def __init__(self, out, indent=0, as_utf8=False, as_one_line=False, + pointy_brackets=False, use_index_order=False, float_format=None, + use_field_number=False): + """Initialize the Printer. + + Floating point values can be formatted compactly with 15 digits of + precision (which is the most that IEEE 754 "double" can guarantee) + using float_format='.15g'. To ensure that converting to text and back to a + proto will result in an identical value, float_format='.17g' should be used. + + Args: + out: To record the text format result. + indent: The indent level for pretty print. + as_utf8: Produce text output in UTF8 format. + as_one_line: Don't introduce newlines between fields. + pointy_brackets: If True, use angle brackets instead of curly braces for + nesting. + use_index_order: If True, print fields of a proto message using the order + defined in source code instead of the field number. By default, use the + field number order. + float_format: If set, use this to specify floating point number formatting + (per the "Format Specification Mini-Language"); otherwise, str() is + used. + use_field_number: If True, print field numbers instead of names. + """ + self.out = out + self.indent = indent + self.as_utf8 = as_utf8 + self.as_one_line = as_one_line + self.pointy_brackets = pointy_brackets + self.use_index_order = use_index_order + self.float_format = float_format + self.use_field_number = use_field_number + + def PrintMessage(self, message): + """Convert protobuf message to text format. + + Args: + message: The protocol buffers message. + """ + fields = message.ListFields() + if self.use_index_order: + fields.sort(key=lambda x: x[0].index) + for field, value in fields: + if _IsMapEntry(field): + for key in sorted(value): + # This is slow for maps with submessage entires because it copies the + # entire tree. Unfortunately this would take significant refactoring + # of this file to work around. + # + # TODO(haberman): refactor and optimize if this becomes an issue. + entry_submsg = field.message_type._concrete_class( + key=key, value=value[key]) + self.PrintField(field, entry_submsg) + elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + for element in value: + self.PrintField(field, element) + else: + self.PrintField(field, value) + + def PrintField(self, field, value): + """Print a single field name/value pair.""" + out = self.out + out.write(' ' * self.indent) + if self.use_field_number: + out.write(str(field.number)) else: - out.write(' %s\n' % openb) - PrintMessage(value, out, indent + 2, as_utf8, as_one_line, - pointy_brackets=pointy_brackets, - use_index_order=use_index_order, - float_format=float_format) - out.write(' ' * indent + closeb) - elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: - enum_value = field.enum_type.values_by_number.get(value, None) - if enum_value is not None: - out.write(enum_value.name) + if field.is_extension: + out.write('[') + if (field.containing_type.GetOptions().message_set_wire_format and + field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and + field.label == descriptor.FieldDescriptor.LABEL_OPTIONAL): + out.write(field.message_type.full_name) + else: + out.write(field.full_name) + out.write(']') + elif field.type == descriptor.FieldDescriptor.TYPE_GROUP: + # For groups, use the capitalized name. + out.write(field.message_type.name) + else: + out.write(field.name) + + if field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + # The colon is optional in this case, but our cross-language golden files + # don't include it. + out.write(': ') + + self.PrintFieldValue(field, value) + if self.as_one_line: + out.write(' ') + else: + out.write('\n') + + def PrintFieldValue(self, field, value): + """Print a single field value (not including name). + + For repeated fields, the value should be a single element. + + Args: + field: The descriptor of the field to be printed. + value: The value of the field. + """ + out = self.out + if self.pointy_brackets: + openb = '<' + closeb = '>' + else: + openb = '{' + closeb = '}' + + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + if self.as_one_line: + out.write(' %s ' % openb) + self.PrintMessage(value) + out.write(closeb) + else: + out.write(' %s\n' % openb) + self.indent += 2 + self.PrintMessage(value) + self.indent -= 2 + out.write(' ' * self.indent + closeb) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: + enum_value = field.enum_type.values_by_number.get(value, None) + if enum_value is not None: + out.write(enum_value.name) + else: + out.write(str(value)) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: + out.write('\"') + if isinstance(value, six.text_type): + out_value = value.encode('utf-8') + else: + out_value = value + if field.type == descriptor.FieldDescriptor.TYPE_BYTES: + # We need to escape non-UTF8 chars in TYPE_BYTES field. + out_as_utf8 = False + else: + out_as_utf8 = self.as_utf8 + out.write(text_encoding.CEscape(out_value, out_as_utf8)) + out.write('\"') + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: + if value: + out.write('true') + else: + out.write('false') + elif field.cpp_type in _FLOAT_TYPES and self.float_format is not None: + out.write('{1:{0}}'.format(self.float_format, value)) else: out.write(str(value)) - elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: - out.write('\"') - if isinstance(value, six.text_type): - out_value = value.encode('utf-8') - else: - out_value = value - if field.type == descriptor.FieldDescriptor.TYPE_BYTES: - # We need to escape non-UTF8 chars in TYPE_BYTES field. - out_as_utf8 = False - else: - out_as_utf8 = as_utf8 - out.write(text_encoding.CEscape(out_value, out_as_utf8)) - out.write('\"') - elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: - if value: - out.write('true') - else: - out.write('false') - elif field.cpp_type in _FLOAT_TYPES and float_format is not None: - out.write('{1:{0}}'.format(float_format, value)) - else: - out.write(str(value)) -def Parse(text, message, allow_unknown_extension=False): +def Parse(text, message, + allow_unknown_extension=False, allow_field_number=False): """Parses an text representation of a protocol message into a message. Args: @@ -276,6 +331,7 @@ def Parse(text, message, allow_unknown_extension=False): message: A protocol buffer message to merge into. allow_unknown_extension: if True, skip over missing extensions and keep parsing + allow_field_number: if True, both field number and field name are allowed. Returns: The same message passed as argument. @@ -285,10 +341,12 @@ def Parse(text, message, allow_unknown_extension=False): """ if not isinstance(text, str): text = text.decode('utf-8') - return ParseLines(text.split('\n'), message, allow_unknown_extension) + return ParseLines(text.split('\n'), message, allow_unknown_extension, + allow_field_number) -def Merge(text, message, allow_unknown_extension=False): +def Merge(text, message, allow_unknown_extension=False, + allow_field_number=False): """Parses an text representation of a protocol message into a message. Like Parse(), but allows repeated values for a non-repeated field, and uses @@ -299,6 +357,7 @@ def Merge(text, message, allow_unknown_extension=False): message: A protocol buffer message to merge into. allow_unknown_extension: if True, skip over missing extensions and keep parsing + allow_field_number: if True, both field number and field name are allowed. Returns: The same message passed as argument. @@ -306,10 +365,12 @@ def Merge(text, message, allow_unknown_extension=False): Raises: ParseError: On text parsing problems. """ - return MergeLines(text.split('\n'), message, allow_unknown_extension) + return MergeLines(text.split('\n'), message, allow_unknown_extension, + allow_field_number) -def ParseLines(lines, message, allow_unknown_extension=False): +def ParseLines(lines, message, allow_unknown_extension=False, + allow_field_number=False): """Parses an text representation of a protocol message into a message. Args: @@ -317,6 +378,7 @@ def ParseLines(lines, message, allow_unknown_extension=False): message: A protocol buffer message to merge into. allow_unknown_extension: if True, skip over missing extensions and keep parsing + allow_field_number: if True, both field number and field name are allowed. Returns: The same message passed as argument. @@ -324,11 +386,12 @@ def ParseLines(lines, message, allow_unknown_extension=False): Raises: ParseError: On text parsing problems. """ - _ParseOrMerge(lines, message, False, allow_unknown_extension) - return message + parser = _Parser(allow_unknown_extension, allow_field_number) + return parser.ParseLines(lines, message) -def MergeLines(lines, message, allow_unknown_extension=False): +def MergeLines(lines, message, allow_unknown_extension=False, + allow_field_number=False): """Parses an text representation of a protocol message into a message. Args: @@ -336,6 +399,7 @@ def MergeLines(lines, message, allow_unknown_extension=False): message: A protocol buffer message to merge into. allow_unknown_extension: if True, skip over missing extensions and keep parsing + allow_field_number: if True, both field number and field name are allowed. Returns: The same message passed as argument. @@ -343,108 +407,174 @@ def MergeLines(lines, message, allow_unknown_extension=False): Raises: ParseError: On text parsing problems. """ - _ParseOrMerge(lines, message, True, allow_unknown_extension) - return message + parser = _Parser(allow_unknown_extension, allow_field_number) + return parser.MergeLines(lines, message) -def _ParseOrMerge(lines, - message, - allow_multiple_scalars, - allow_unknown_extension=False): - """Converts an text representation of a protocol message into a message. +class _Parser(object): + """Text format parser for protocol message.""" - Args: - lines: Lines of a message's text representation. - message: A protocol buffer message to merge into. - allow_multiple_scalars: Determines if repeated values for a non-repeated - field are permitted, e.g., the string "foo: 1 foo: 2" for a - required/optional field named "foo". - allow_unknown_extension: if True, skip over missing extensions and keep - parsing + def __init__(self, allow_unknown_extension=False, allow_field_number=False): + self.allow_unknown_extension = allow_unknown_extension + self.allow_field_number = allow_field_number - Raises: - ParseError: On text parsing problems. - """ - tokenizer = _Tokenizer(lines) - while not tokenizer.AtEnd(): - _MergeField(tokenizer, message, allow_multiple_scalars, - allow_unknown_extension) + def ParseFromString(self, text, message): + """Parses an text representation of a protocol message into a message.""" + if not isinstance(text, str): + text = text.decode('utf-8') + return self.ParseLines(text.split('\n'), message) + def ParseLines(self, lines, message): + """Parses an text representation of a protocol message into a message.""" + self._allow_multiple_scalars = False + self._ParseOrMerge(lines, message) + return message -def _MergeField(tokenizer, - message, - allow_multiple_scalars, - allow_unknown_extension=False): - """Merges a single protocol message field into a message. + def MergeFromString(self, text, message): + """Merges an text representation of a protocol message into a message.""" + return self._MergeLines(text.split('\n'), message) - Args: - tokenizer: A tokenizer to parse the field name and values. - message: A protocol message to record the data. - allow_multiple_scalars: Determines if repeated values for a non-repeated - field are permitted, e.g., the string "foo: 1 foo: 2" for a - required/optional field named "foo". - allow_unknown_extension: if True, skip over missing extensions and keep - parsing + def MergeLines(self, lines, message): + """Merges an text representation of a protocol message into a message.""" + self._allow_multiple_scalars = True + self._ParseOrMerge(lines, message) + return message - Raises: - ParseError: In case of text parsing problems. - """ - message_descriptor = message.DESCRIPTOR - if (hasattr(message_descriptor, 'syntax') and - message_descriptor.syntax == 'proto3'): - # Proto3 doesn't represent presence so we can't test if multiple - # scalars have occurred. We have to allow them. - allow_multiple_scalars = True - if tokenizer.TryConsume('['): - name = [tokenizer.ConsumeIdentifier()] - while tokenizer.TryConsume('.'): - name.append(tokenizer.ConsumeIdentifier()) - name = '.'.join(name) + def _ParseOrMerge(self, lines, message): + """Converts an text representation of a protocol message into a message. - if not message_descriptor.is_extendable: - raise tokenizer.ParseErrorPreviousToken( - 'Message type "%s" does not have extensions.' % - message_descriptor.full_name) - # pylint: disable=protected-access - field = message.Extensions._FindExtensionByName(name) - # pylint: enable=protected-access - if not field: - if allow_unknown_extension: - field = None - else: + Args: + lines: Lines of a message's text representation. + message: A protocol buffer message to merge into. + + Raises: + ParseError: On text parsing problems. + """ + tokenizer = _Tokenizer(lines) + while not tokenizer.AtEnd(): + self._MergeField(tokenizer, message) + + def _MergeField(self, tokenizer, message): + """Merges a single protocol message field into a message. + + Args: + tokenizer: A tokenizer to parse the field name and values. + message: A protocol message to record the data. + + Raises: + ParseError: In case of text parsing problems. + """ + message_descriptor = message.DESCRIPTOR + if (hasattr(message_descriptor, 'syntax') and + message_descriptor.syntax == 'proto3'): + # Proto3 doesn't represent presence so we can't test if multiple + # scalars have occurred. We have to allow them. + self._allow_multiple_scalars = True + if tokenizer.TryConsume('['): + name = [tokenizer.ConsumeIdentifier()] + while tokenizer.TryConsume('.'): + name.append(tokenizer.ConsumeIdentifier()) + name = '.'.join(name) + + if not message_descriptor.is_extendable: raise tokenizer.ParseErrorPreviousToken( - 'Extension "%s" not registered.' % name) - elif message_descriptor != field.containing_type: - raise tokenizer.ParseErrorPreviousToken( - 'Extension "%s" does not extend message type "%s".' % ( - name, message_descriptor.full_name)) + 'Message type "%s" does not have extensions.' % + message_descriptor.full_name) + # pylint: disable=protected-access + field = message.Extensions._FindExtensionByName(name) + # pylint: enable=protected-access + if not field: + if self.allow_unknown_extension: + field = None + else: + raise tokenizer.ParseErrorPreviousToken( + 'Extension "%s" not registered.' % name) + elif message_descriptor != field.containing_type: + raise tokenizer.ParseErrorPreviousToken( + 'Extension "%s" does not extend message type "%s".' % ( + name, message_descriptor.full_name)) - tokenizer.Consume(']') + tokenizer.Consume(']') - else: - name = tokenizer.ConsumeIdentifier() - field = message_descriptor.fields_by_name.get(name, None) + else: + name = tokenizer.ConsumeIdentifier() + if self.allow_field_number and name.isdigit(): + number = ParseInteger(name, True, True) + field = message_descriptor.fields_by_number.get(number, None) + if not field and message_descriptor.is_extendable: + field = message.Extensions._FindExtensionByNumber(number) + else: + field = message_descriptor.fields_by_name.get(name, None) - # Group names are expected to be capitalized as they appear in the - # .proto file, which actually matches their type names, not their field - # names. - if not field: - field = message_descriptor.fields_by_name.get(name.lower(), None) - if field and field.type != descriptor.FieldDescriptor.TYPE_GROUP: - field = None + # Group names are expected to be capitalized as they appear in the + # .proto file, which actually matches their type names, not their field + # names. + if not field: + field = message_descriptor.fields_by_name.get(name.lower(), None) + if field and field.type != descriptor.FieldDescriptor.TYPE_GROUP: + field = None - if (field and field.type == descriptor.FieldDescriptor.TYPE_GROUP and - field.message_type.name != name): - field = None + if (field and field.type == descriptor.FieldDescriptor.TYPE_GROUP and + field.message_type.name != name): + field = None - if not field: - raise tokenizer.ParseErrorPreviousToken( - 'Message type "%s" has no field named "%s".' % ( - message_descriptor.full_name, name)) + if not field: + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" has no field named "%s".' % ( + message_descriptor.full_name, name)) - if field and field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + if field: + if not self._allow_multiple_scalars and field.containing_oneof: + # Check if there's a different field set in this oneof. + # Note that we ignore the case if the same field was set before, and we + # apply _allow_multiple_scalars to non-scalar fields as well. + which_oneof = message.WhichOneof(field.containing_oneof.name) + if which_oneof is not None and which_oneof != field.name: + raise tokenizer.ParseErrorPreviousToken( + 'Field "%s" is specified along with field "%s", another member ' + 'of oneof "%s" for message type "%s".' % ( + field.name, which_oneof, field.containing_oneof.name, + message_descriptor.full_name)) + + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + tokenizer.TryConsume(':') + merger = self._MergeMessageField + else: + tokenizer.Consume(':') + merger = self._MergeScalarField + + if (field.label == descriptor.FieldDescriptor.LABEL_REPEATED + and tokenizer.TryConsume('[')): + # Short repeated format, e.g. "foo: [1, 2, 3]" + while True: + merger(tokenizer, message, field) + if tokenizer.TryConsume(']'): break + tokenizer.Consume(',') + + else: + merger(tokenizer, message, field) + + else: # Proto field is unknown. + assert self.allow_unknown_extension + _SkipFieldContents(tokenizer) + + # For historical reasons, fields may optionally be separated by commas or + # semicolons. + if not tokenizer.TryConsume(','): + tokenizer.TryConsume(';') + + def _MergeMessageField(self, tokenizer, message, field): + """Merges a single scalar field into a message. + + Args: + tokenizer: A tokenizer to parse the field value. + message: The message of which field is a member. + field: The descriptor of the field to be merged. + + Raises: + ParseError: In case of text parsing problems. + """ is_map_entry = _IsMapEntry(field) - tokenizer.TryConsume(':') if tokenizer.TryConsume('<'): end_token = '>' @@ -456,6 +586,7 @@ def _MergeField(tokenizer, if field.is_extension: sub_message = message.Extensions[field].add() elif is_map_entry: + # pylint: disable=protected-access sub_message = field.message_type._concrete_class() else: sub_message = getattr(message, field.name).add() @@ -468,9 +599,8 @@ def _MergeField(tokenizer, while not tokenizer.TryConsume(end_token): if tokenizer.AtEnd(): - raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % (end_token)) - _MergeField(tokenizer, sub_message, allow_multiple_scalars, - allow_unknown_extension) + raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % (end_token,)) + self._MergeField(tokenizer, sub_message) if is_map_entry: value_cpptype = field.message_type.fields_by_name['value'].cpp_type @@ -479,26 +609,70 @@ def _MergeField(tokenizer, value.MergeFrom(sub_message.value) else: getattr(message, field.name)[sub_message.key] = sub_message.value - elif field: - tokenizer.Consume(':') - if (field.label == descriptor.FieldDescriptor.LABEL_REPEATED and - tokenizer.TryConsume('[')): - # Short repeated format, e.g. "foo: [1, 2, 3]" - while True: - _MergeScalarField(tokenizer, message, field, allow_multiple_scalars) - if tokenizer.TryConsume(']'): - break - tokenizer.Consume(',') - else: - _MergeScalarField(tokenizer, message, field, allow_multiple_scalars) - else: # Proto field is unknown. - assert allow_unknown_extension - _SkipFieldContents(tokenizer) - # For historical reasons, fields may optionally be separated by commas or - # semicolons. - if not tokenizer.TryConsume(','): - tokenizer.TryConsume(';') + def _MergeScalarField(self, tokenizer, message, field): + """Merges a single scalar field into a message. + + Args: + tokenizer: A tokenizer to parse the field value. + message: A protocol message to record the data. + field: The descriptor of the field to be merged. + + Raises: + ParseError: In case of text parsing problems. + RuntimeError: On runtime errors. + """ + _ = self.allow_unknown_extension + value = None + + if field.type in (descriptor.FieldDescriptor.TYPE_INT32, + descriptor.FieldDescriptor.TYPE_SINT32, + descriptor.FieldDescriptor.TYPE_SFIXED32): + value = tokenizer.ConsumeInt32() + elif field.type in (descriptor.FieldDescriptor.TYPE_INT64, + descriptor.FieldDescriptor.TYPE_SINT64, + descriptor.FieldDescriptor.TYPE_SFIXED64): + value = tokenizer.ConsumeInt64() + elif field.type in (descriptor.FieldDescriptor.TYPE_UINT32, + descriptor.FieldDescriptor.TYPE_FIXED32): + value = tokenizer.ConsumeUint32() + elif field.type in (descriptor.FieldDescriptor.TYPE_UINT64, + descriptor.FieldDescriptor.TYPE_FIXED64): + value = tokenizer.ConsumeUint64() + elif field.type in (descriptor.FieldDescriptor.TYPE_FLOAT, + descriptor.FieldDescriptor.TYPE_DOUBLE): + value = tokenizer.ConsumeFloat() + elif field.type == descriptor.FieldDescriptor.TYPE_BOOL: + value = tokenizer.ConsumeBool() + elif field.type == descriptor.FieldDescriptor.TYPE_STRING: + value = tokenizer.ConsumeString() + elif field.type == descriptor.FieldDescriptor.TYPE_BYTES: + value = tokenizer.ConsumeByteString() + elif field.type == descriptor.FieldDescriptor.TYPE_ENUM: + value = tokenizer.ConsumeEnum(field) + else: + raise RuntimeError('Unknown field type %d' % field.type) + + if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + if field.is_extension: + message.Extensions[field].append(value) + else: + getattr(message, field.name).append(value) + else: + if field.is_extension: + if not self._allow_multiple_scalars and message.HasExtension(field): + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" extensions.' % + (message.DESCRIPTOR.full_name, field.full_name)) + else: + message.Extensions[field] = value + else: + if not self._allow_multiple_scalars and message.HasField(field.name): + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" fields.' % + (message.DESCRIPTOR.full_name, field.name)) + else: + setattr(message, field.name, value) def _SkipFieldContents(tokenizer): @@ -571,10 +745,10 @@ def _SkipFieldValue(tokenizer): Raises: ParseError: In case an invalid field value is found. """ - # String tokens can come in multiple adjacent string literals. + # String/bytes tokens can come in multiple adjacent string literals. # If we can consume one, consume as many as we can. - if tokenizer.TryConsumeString(): - while tokenizer.TryConsumeString(): + if tokenizer.TryConsumeByteString(): + while tokenizer.TryConsumeByteString(): pass return @@ -585,73 +759,6 @@ def _SkipFieldValue(tokenizer): raise ParseError('Invalid field value: ' + tokenizer.token) -def _MergeScalarField(tokenizer, message, field, allow_multiple_scalars): - """Merges a single protocol message scalar field into a message. - - Args: - tokenizer: A tokenizer to parse the field value. - message: A protocol message to record the data. - field: The descriptor of the field to be merged. - allow_multiple_scalars: Determines if repeated values for a non-repeated - field are permitted, e.g., the string "foo: 1 foo: 2" for a - required/optional field named "foo". - - Raises: - ParseError: In case of text parsing problems. - RuntimeError: On runtime errors. - """ - value = None - - if field.type in (descriptor.FieldDescriptor.TYPE_INT32, - descriptor.FieldDescriptor.TYPE_SINT32, - descriptor.FieldDescriptor.TYPE_SFIXED32): - value = tokenizer.ConsumeInt32() - elif field.type in (descriptor.FieldDescriptor.TYPE_INT64, - descriptor.FieldDescriptor.TYPE_SINT64, - descriptor.FieldDescriptor.TYPE_SFIXED64): - value = tokenizer.ConsumeInt64() - elif field.type in (descriptor.FieldDescriptor.TYPE_UINT32, - descriptor.FieldDescriptor.TYPE_FIXED32): - value = tokenizer.ConsumeUint32() - elif field.type in (descriptor.FieldDescriptor.TYPE_UINT64, - descriptor.FieldDescriptor.TYPE_FIXED64): - value = tokenizer.ConsumeUint64() - elif field.type in (descriptor.FieldDescriptor.TYPE_FLOAT, - descriptor.FieldDescriptor.TYPE_DOUBLE): - value = tokenizer.ConsumeFloat() - elif field.type == descriptor.FieldDescriptor.TYPE_BOOL: - value = tokenizer.ConsumeBool() - elif field.type == descriptor.FieldDescriptor.TYPE_STRING: - value = tokenizer.ConsumeString() - elif field.type == descriptor.FieldDescriptor.TYPE_BYTES: - value = tokenizer.ConsumeByteString() - elif field.type == descriptor.FieldDescriptor.TYPE_ENUM: - value = tokenizer.ConsumeEnum(field) - else: - raise RuntimeError('Unknown field type %d' % field.type) - - if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: - if field.is_extension: - message.Extensions[field].append(value) - else: - getattr(message, field.name).append(value) - else: - if field.is_extension: - if not allow_multiple_scalars and message.HasExtension(field): - raise tokenizer.ParseErrorPreviousToken( - 'Message type "%s" should not have multiple "%s" extensions.' % - (message.DESCRIPTOR.full_name, field.full_name)) - else: - message.Extensions[field] = value - else: - if not allow_multiple_scalars and message.HasField(field.name): - raise tokenizer.ParseErrorPreviousToken( - 'Message type "%s" should not have multiple "%s" fields.' % - (message.DESCRIPTOR.full_name, field.name)) - else: - setattr(message, field.name, value) - - class _Tokenizer(object): """Protocol buffer text representation tokenizer. @@ -882,9 +989,9 @@ class _Tokenizer(object): self.NextToken() return result - def TryConsumeString(self): + def TryConsumeByteString(self): try: - self.ConsumeString() + self.ConsumeByteString() return True except ParseError: return False diff --git a/python/setup.py b/python/setup.py index 6ea3bad73..0f4b53c4a 100755 --- a/python/setup.py +++ b/python/setup.py @@ -157,13 +157,30 @@ class test_conformance(_build_py): status = subprocess.check_call(cmd, shell=True) +def get_option_from_sys_argv(option_str): + if option_str in sys.argv: + sys.argv.remove(option_str) + return True + return False + + if __name__ == '__main__': ext_module_list = [] - cpp_impl = '--cpp_implementation' warnings_as_errors = '--warnings_as_errors' - if cpp_impl in sys.argv: - sys.argv.remove(cpp_impl) - extra_compile_args = ['-Wno-write-strings', '-Wno-invalid-offsetof'] + if get_option_from_sys_argv('--cpp_implementation'): + # Link libprotobuf.a and libprotobuf-lite.a statically with the + # extension. Note that those libraries have to be compiled with + # -fPIC for this to work. + compile_static_ext = get_option_from_sys_argv('--compile_static_extension') + extra_compile_args = ['-Wno-write-strings', + '-Wno-invalid-offsetof', + '-Wno-sign-compare'] + libraries = ['protobuf'] + extra_objects = None + if compile_static_ext: + libraries = None + extra_objects = ['../src/.libs/libprotobuf.a', + '../src/.libs/libprotobuf-lite.a'] test_conformance.target = 'test_python_cpp' if "clang" in os.popen('$CC --version 2> /dev/null').read(): @@ -174,16 +191,22 @@ if __name__ == '__main__': sys.argv.remove(warnings_as_errors) # C++ implementation extension - ext_module_list.append( + ext_module_list.extend([ Extension( "google.protobuf.pyext._message", glob.glob('google/protobuf/pyext/*.cc'), include_dirs=[".", "../src"], - libraries=['protobuf'], + libraries=libraries, + extra_objects=extra_objects, library_dirs=['../src/.libs'], extra_compile_args=extra_compile_args, - ) - ) + ), + Extension( + "google.protobuf.internal._api_implementation", + glob.glob('google/protobuf/internal/api_implementation.cc'), + extra_compile_args=['-DPYTHON_PROTO2_CPP_IMPL_V2'], + ), + ]) os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'cpp' # Keep this list of dependencies in sync with tox.ini. diff --git a/ruby/Gemfile.lock b/ruby/Gemfile.lock index 27e575069..d0eb9cc42 100644 --- a/ruby/Gemfile.lock +++ b/ruby/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - google-protobuf (3.0.0.alpha.5.0) + google-protobuf (3.0.0.alpha.5.0.5) GEM remote: https://rubygems.org/ diff --git a/ruby/README.md b/ruby/README.md index 16474322e..f28e05a72 100644 --- a/ruby/README.md +++ b/ruby/README.md @@ -32,23 +32,25 @@ documentation may be found in the RubyDoc comments (`call-seq` tags) in the source, and we plan to release separate, more detailed, documentation at a later date. - require 'google/protobuf' +```ruby +require 'google/protobuf' - # generated from my_proto_types.proto with protoc: - # $ protoc --ruby_out=. my_proto_types.proto - require 'my_proto_types' +# generated from my_proto_types.proto with protoc: +# $ protoc --ruby_out=. my_proto_types.proto +require 'my_proto_types' - mymessage = MyTestMessage.new(:field1 => 42, :field2 => ["a", "b", "c"]) - mymessage.field1 = 43 - mymessage.field2.push("d") - mymessage.field3 = SubMessage.new(:foo => 100) +mymessage = MyTestMessage.new(:field1 => 42, :field2 => ["a", "b", "c"]) +mymessage.field1 = 43 +mymessage.field2.push("d") +mymessage.field3 = SubMessage.new(:foo => 100) - encoded_data = MyTestMessage.encode(mymessage) - decoded = MyTestMessage.decode(encoded_data) - assert decoded == mymessage +encoded_data = MyTestMessage.encode(mymessage) +decoded = MyTestMessage.decode(encoded_data) +assert decoded == mymessage - puts "JSON:" - puts MyTestMessage.encode_json(mymessage) +puts "JSON:" +puts MyTestMessage.encode_json(mymessage) +``` Installation from Source (Building Gem) --------------------------------------- diff --git a/ruby/Rakefile b/ruby/Rakefile index 8eb7a2df3..fa29c3151 100644 --- a/ruby/Rakefile +++ b/ruby/Rakefile @@ -5,6 +5,40 @@ require "rake/testtask" spec = Gem::Specification.load("google-protobuf.gemspec") +well_known_protos = %w[ + google/protobuf/any.proto + google/protobuf/api.proto + google/protobuf/duration.proto + google/protobuf/empty.proto + google/protobuf/field_mask.proto + google/protobuf/source_context.proto + google/protobuf/struct.proto + google/protobuf/timestamp.proto + google/protobuf/type.proto + google/protobuf/wrappers.proto +] + +# These are omitted for now because we don't support proto2. +proto2_protos = %w[ + google/protobuf/descriptor.proto + google/protobuf/compiler/plugin.proto +] + +genproto_output = [] + +# We won't have access to .. from within docker, but the proto files +# will be there, thanks to the :genproto rule dependency for gem:native. +unless ENV['IN_DOCKER'] == 'true' + well_known_protos.each do |proto_file| + input_file = "../src/" + proto_file + output_file = "lib/" + proto_file.sub(/\.proto$/, ".rb") + genproto_output << output_file + file output_file => input_file do |file_task| + sh "../src/protoc -I../src --ruby_out=lib #{input_file}" + end + end +end + if RUBY_PLATFORM == "java" if `which mvn` == '' raise ArgumentError, "maven needs to be installed" @@ -30,37 +64,16 @@ else task 'gem:windows' do require 'rake_compiler_dock' - RakeCompilerDock.sh "bundle && rake cross native gem RUBY_CC_VERSION=2.3.0:2.2.2:2.1.6" + RakeCompilerDock.sh "bundle && IN_DOCKER=true rake cross native gem RUBY_CC_VERSION=2.3.0:2.2.2:2.1.5:2.0.0" end -end -well_known_protos = %w[ - google/protobuf/any.proto - google/protobuf/api.proto - google/protobuf/duration.proto - google/protobuf/empty.proto - google/protobuf/field_mask.proto - google/protobuf/source_context.proto - google/protobuf/struct.proto - google/protobuf/timestamp.proto - google/protobuf/type.proto - google/protobuf/wrappers.proto -] - -# These are omitted for now because we don't support proto2. -proto2_protos = %w[ - google/protobuf/descriptor.proto - google/protobuf/compiler/plugin.proto -] - -genproto_output = [] - -well_known_protos.each do |proto_file| - input_file = "../src/" + proto_file - output_file = "lib/" + proto_file.sub(/\.proto$/, ".rb") - genproto_output << output_file - file output_file => input_file do |file_task| - sh "../src/protoc -I../src --ruby_out=lib #{input_file}" + if RUBY_PLATFORM =~ /darwin/ + task 'gem:native' do + system "rake genproto" + system "rake cross native gem RUBY_CC_VERSION=2.3.0:2.2.2:2.1.5:2.0.0" + end + else + task 'gem:native' => [:genproto, 'gem:windows'] end end diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index 96ef4953b..7e93bafb3 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -255,6 +255,10 @@ void Descriptor_free(void* _self) { upb_handlers_unref(self->json_serialize_handlers, &self->json_serialize_handlers); } + if (self->json_serialize_handlers_preserve) { + upb_handlers_unref(self->json_serialize_handlers_preserve, + &self->json_serialize_handlers_preserve); + } xfree(self); } @@ -278,6 +282,7 @@ VALUE Descriptor_alloc(VALUE klass) { self->json_fill_method = NULL; self->pb_serialize_handlers = NULL; self->json_serialize_handlers = NULL; + self->json_serialize_handlers_preserve = NULL; self->typeclass_references = rb_ary_new(); return ret; } diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index c2c369ebb..f6bea50f3 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -656,7 +656,6 @@ static const upb_json_parsermethod *msgdef_jsonparsermethod(Descriptor* desc) { #define STACK_ENV_STACKBYTES 4096 typedef struct { upb_env env; - upb_seededalloc alloc; const char* ruby_error_template; char allocbuf[STACK_ENV_STACKBYTES]; } stackenv; @@ -681,16 +680,12 @@ static bool env_error_func(void* ud, const upb_status* status) { static void stackenv_init(stackenv* se, const char* errmsg) { se->ruby_error_template = errmsg; - upb_env_init(&se->env); - upb_seededalloc_init(&se->alloc, &se->allocbuf, STACK_ENV_STACKBYTES); - upb_env_setallocfunc( - &se->env, upb_seededalloc_getallocfunc(&se->alloc), &se->alloc); + upb_env_init2(&se->env, se->allocbuf, sizeof(se->allocbuf), NULL); upb_env_seterrorfunc(&se->env, env_error_func, se); } static void stackenv_uninit(stackenv* se) { upb_env_uninit(&se->env); - upb_seededalloc_uninit(&se->alloc); } /* @@ -1130,13 +1125,23 @@ static const upb_handlers* msgdef_pb_serialize_handlers(Descriptor* desc) { return desc->pb_serialize_handlers; } -static const upb_handlers* msgdef_json_serialize_handlers(Descriptor* desc) { - if (desc->json_serialize_handlers == NULL) { - desc->json_serialize_handlers = - upb_json_printer_newhandlers( - desc->msgdef, &desc->json_serialize_handlers); +static const upb_handlers* msgdef_json_serialize_handlers( + Descriptor* desc, bool preserve_proto_fieldnames) { + if (preserve_proto_fieldnames) { + if (desc->json_serialize_handlers == NULL) { + desc->json_serialize_handlers = + upb_json_printer_newhandlers( + desc->msgdef, true, &desc->json_serialize_handlers); + } + return desc->json_serialize_handlers; + } else { + if (desc->json_serialize_handlers_preserve == NULL) { + desc->json_serialize_handlers_preserve = + upb_json_printer_newhandlers( + desc->msgdef, false, &desc->json_serialize_handlers_preserve); + } + return desc->json_serialize_handlers_preserve; } - return desc->json_serialize_handlers; } /* @@ -1181,16 +1186,33 @@ VALUE Message_encode(VALUE klass, VALUE msg_rb) { * * Encodes the given message object into its serialized JSON representation. */ -VALUE Message_encode_json(VALUE klass, VALUE msg_rb) { +VALUE Message_encode_json(int argc, VALUE* argv, VALUE klass) { VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); Descriptor* desc = ruby_to_Descriptor(descriptor); - + VALUE msg_rb; + VALUE preserve_proto_fieldnames = Qfalse; stringsink sink; + + if (argc < 1 || argc > 2) { + rb_raise(rb_eArgError, "Expected 1 or 2 arguments."); + } + + msg_rb = argv[0]; + + if (argc == 2) { + VALUE hash_args = argv[1]; + if (TYPE(hash_args) != T_HASH) { + rb_raise(rb_eArgError, "Expected hash arguments."); + } + preserve_proto_fieldnames = rb_hash_lookup2( + hash_args, ID2SYM(rb_intern("preserve_proto_fieldnames")), Qfalse); + } + stringsink_init(&sink); { const upb_handlers* serialize_handlers = - msgdef_json_serialize_handlers(desc); + msgdef_json_serialize_handlers(desc, RTEST(preserve_proto_fieldnames)); upb_json_printer* printer; stackenv se; VALUE ret; diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index 283939c9d..e16250f3b 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -151,32 +151,30 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) { name_len--; } - // Check for a oneof name first. - o = upb_msgdef_ntoo(self->descriptor->msgdef, - name, name_len); + // See if this name corresponds to either a oneof or field in this message. + if (!upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len, &f, + &o)) { + return rb_call_super(argc, argv); + } + if (o != NULL) { + // This is a oneof -- return which field inside the oneof is set. if (setter) { rb_raise(rb_eRuntimeError, "Oneof accessors are read-only."); } return which_oneof_field(self, o); - } - - // Otherwise, check for a field with that name. - f = upb_msgdef_ntof(self->descriptor->msgdef, - name, name_len); - - if (f == NULL) { - return rb_call_super(argc, argv); - } - - if (setter) { - if (argc < 2) { - rb_raise(rb_eArgError, "No value provided to setter."); - } - layout_set(self->descriptor->layout, Message_data(self), f, argv[1]); - return Qnil; } else { - return layout_get(self->descriptor->layout, Message_data(self), f); + // This is a field -- get or set the field's value. + assert(f); + if (setter) { + if (argc < 2) { + rb_raise(rb_eArgError, "No value provided to setter."); + } + layout_set(self->descriptor->layout, Message_data(self), f, argv[1]); + return Qnil; + } else { + return layout_get(self->descriptor->layout, Message_data(self), f); + } } } @@ -475,7 +473,7 @@ VALUE build_class_from_descriptor(Descriptor* desc) { rb_define_singleton_method(klass, "decode", Message_decode, 1); rb_define_singleton_method(klass, "encode", Message_encode, 1); rb_define_singleton_method(klass, "decode_json", Message_decode_json, 1); - rb_define_singleton_method(klass, "encode_json", Message_encode_json, 1); + rb_define_singleton_method(klass, "encode_json", Message_encode_json, -1); rb_define_singleton_method(klass, "descriptor", Message_descriptor, 0); return klass; diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index 21ce7bb33..2834c8944 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -115,6 +115,7 @@ struct Descriptor { const upb_json_parsermethod* json_fill_method; const upb_handlers* pb_serialize_handlers; const upb_handlers* json_serialize_handlers; + const upb_handlers* json_serialize_handlers_preserve; // Handlers hold type class references for sub-message fields directly in some // cases. We need to keep these rooted because they might otherwise be // collected. @@ -498,7 +499,7 @@ VALUE Message_descriptor(VALUE klass); VALUE Message_decode(VALUE klass, VALUE data); VALUE Message_encode(VALUE klass, VALUE msg_rb); VALUE Message_decode_json(VALUE klass, VALUE data); -VALUE Message_encode_json(VALUE klass, VALUE msg_rb); +VALUE Message_encode_json(int argc, VALUE* argv, VALUE klass); VALUE Google_Protobuf_deep_copy(VALUE self, VALUE obj); diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c index b2a69b667..74a2a1db3 100644 --- a/ruby/ext/google/protobuf_c/upb.c +++ b/ruby/ext/google/protobuf_c/upb.c @@ -12,7 +12,7 @@ typedef struct { } str_t; static str_t *newstr(const char *data, size_t len) { - str_t *ret = malloc(sizeof(*ret) + len); + str_t *ret = upb_gmalloc(sizeof(*ret) + len); if (!ret) return NULL; ret->len = len; memcpy(ret->str, data, len); @@ -20,7 +20,7 @@ static str_t *newstr(const char *data, size_t len) { return ret; } -static void freestr(str_t *s) { free(s); } +static void freestr(str_t *s) { upb_gfree(s); } /* isalpha() etc. from are locale-dependent, which we don't want. */ static bool upb_isbetween(char c, char low, char high) { @@ -65,6 +65,22 @@ static bool upb_isident(const char *str, size_t len, bool full, upb_status *s) { return !start; } +static bool upb_isoneof(const upb_refcounted *def) { + return def->vtbl == &upb_oneofdef_vtbl; +} + +static bool upb_isfield(const upb_refcounted *def) { + return def->vtbl == &upb_fielddef_vtbl; +} + +static const upb_oneofdef *upb_trygetoneof(const upb_refcounted *def) { + return upb_isoneof(def) ? (const upb_oneofdef*)def : NULL; +} + +static const upb_fielddef *upb_trygetfield(const upb_refcounted *def) { + return upb_isfield(def) ? (const upb_fielddef*)def : NULL; +} + /* upb_def ********************************************************************/ @@ -72,14 +88,39 @@ upb_deftype_t upb_def_type(const upb_def *d) { return d->type; } const char *upb_def_fullname(const upb_def *d) { return d->fullname; } +const char *upb_def_name(const upb_def *d) { + const char *p; + + if (d->fullname == NULL) { + return NULL; + } else if ((p = strrchr(d->fullname, '.')) == NULL) { + /* No '.' in the name, return the full string. */ + return d->fullname; + } else { + /* Return one past the last '.'. */ + return p + 1; + } +} + bool upb_def_setfullname(upb_def *def, const char *fullname, upb_status *s) { assert(!upb_def_isfrozen(def)); - if (!upb_isident(fullname, strlen(fullname), true, s)) return false; - free((void*)def->fullname); - def->fullname = upb_strdup(fullname); + if (!upb_isident(fullname, strlen(fullname), true, s)) { + return false; + } + + fullname = upb_gstrdup(fullname); + if (!fullname) { + upb_upberr_setoom(s); + return false; + } + + upb_gfree((void*)def->fullname); + def->fullname = fullname; return true; } +const upb_filedef *upb_def_file(const upb_def *d) { return d->file; } + upb_def *upb_def_dup(const upb_def *def, const void *o) { switch (def->type) { case UPB_DEF_MSG: @@ -102,11 +143,12 @@ static bool upb_def_init(upb_def *def, upb_deftype_t type, def->type = type; def->fullname = NULL; def->came_from_user = false; + def->file = NULL; return true; } static void upb_def_uninit(upb_def *def) { - free((void*)def->fullname); + upb_gfree((void*)def->fullname); } static const char *msgdef_name(const upb_msgdef *m) { @@ -243,8 +285,19 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { int i; uint32_t selector; int n = upb_msgdef_numfields(m); - upb_fielddef **fields = malloc(n * sizeof(*fields)); - if (!fields) return false; + upb_fielddef **fields; + + if (n == 0) { + m->selector_count = UPB_STATIC_SELECTOR_COUNT; + m->submsg_field_count = 0; + return true; + } + + fields = upb_gmalloc(n * sizeof(*fields)); + if (!fields) { + upb_upberr_setoom(s); + return false; + } m->submsg_field_count = 0; for(i = 0, upb_msg_field_begin(&j, m); @@ -253,7 +306,7 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { upb_fielddef *f = upb_msg_iter_field(&j); assert(f->msg.def == m); if (!upb_validate_field(f, s)) { - free(fields); + upb_gfree(fields); return false; } if (upb_fielddef_issubmsg(f)) { @@ -313,15 +366,12 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { #undef TRY #endif - free(fields); + upb_gfree(fields); return true; } -bool upb_def_freeze(upb_def *const* defs, int n, upb_status *s) { - int i; - int maxdepth; - bool ret; - upb_status_clear(s); +bool _upb_def_validate(upb_def *const*defs, size_t n, upb_status *s) { + size_t i; /* First perform validation, in two passes so we can check that we have a * transitive closure without needing to search. */ @@ -347,8 +397,9 @@ bool upb_def_freeze(upb_def *const* defs, int n, upb_status *s) { /* Second pass of validation. Also assign selector bases and indexes, and * compact tables. */ for (i = 0; i < n; i++) { - upb_msgdef *m = upb_dyncast_msgdef_mutable(defs[i]); - upb_enumdef *e = upb_dyncast_enumdef_mutable(defs[i]); + upb_def *def = defs[i]; + upb_msgdef *m = upb_dyncast_msgdef_mutable(def); + upb_enumdef *e = upb_dyncast_enumdef_mutable(def); if (m) { upb_inttable_compact(&m->itof); if (!assign_msg_indices(m, s)) { @@ -359,23 +410,31 @@ bool upb_def_freeze(upb_def *const* defs, int n, upb_status *s) { } } - /* Def graph contains FieldDefs between each MessageDef, so double the - * limit. */ - maxdepth = UPB_MAX_MESSAGE_DEPTH * 2; - - /* Validation all passed; freeze the defs. */ - ret = upb_refcounted_freeze((upb_refcounted * const *)defs, n, s, maxdepth); - assert(!(s && ret != upb_ok(s))); - return ret; + return true; err: for (i = 0; i < n; i++) { - defs[i]->came_from_user = false; + upb_def *def = defs[i]; + def->came_from_user = false; } assert(!(s && upb_ok(s))); return false; } +bool upb_def_freeze(upb_def *const* defs, size_t n, upb_status *s) { + /* Def graph contains FieldDefs between each MessageDef, so double the + * limit. */ + const size_t maxdepth = UPB_MAX_MESSAGE_DEPTH * 2; + + if (!_upb_def_validate(defs, n, s)) { + return false; + } + + + /* Validation all passed; freeze the objects. */ + return upb_refcounted_freeze((upb_refcounted *const*)defs, n, s, maxdepth); +} + /* upb_enumdef ****************************************************************/ @@ -384,21 +443,26 @@ static void upb_enumdef_free(upb_refcounted *r) { upb_inttable_iter i; upb_inttable_begin(&i, &e->iton); for( ; !upb_inttable_done(&i); upb_inttable_next(&i)) { - /* To clean up the upb_strdup() from upb_enumdef_addval(). */ - free(upb_value_getcstr(upb_inttable_iter_value(&i))); + /* To clean up the upb_gstrdup() from upb_enumdef_addval(). */ + upb_gfree(upb_value_getcstr(upb_inttable_iter_value(&i))); } upb_strtable_uninit(&e->ntoi); upb_inttable_uninit(&e->iton); upb_def_uninit(upb_enumdef_upcast_mutable(e)); - free(e); + upb_gfree(e); } +const struct upb_refcounted_vtbl upb_enumdef_vtbl = {NULL, &upb_enumdef_free}; + upb_enumdef *upb_enumdef_new(const void *owner) { - static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_enumdef_free}; - upb_enumdef *e = malloc(sizeof(*e)); + upb_enumdef *e = upb_gmalloc(sizeof(*e)); if (!e) return NULL; - if (!upb_def_init(upb_enumdef_upcast_mutable(e), UPB_DEF_ENUM, &vtbl, owner)) + + if (!upb_def_init(upb_enumdef_upcast_mutable(e), UPB_DEF_ENUM, + &upb_enumdef_vtbl, owner)) { goto err2; + } + if (!upb_strtable_init(&e->ntoi, UPB_CTYPE_INT32)) goto err2; if (!upb_inttable_init(&e->iton, UPB_CTYPE_CSTR)) goto err1; return e; @@ -406,7 +470,7 @@ upb_enumdef *upb_enumdef_new(const void *owner) { err1: upb_strtable_uninit(&e->ntoi); err2: - free(e); + upb_gfree(e); return NULL; } @@ -434,6 +498,10 @@ const char *upb_enumdef_fullname(const upb_enumdef *e) { return upb_def_fullname(upb_enumdef_upcast(e)); } +const char *upb_enumdef_name(const upb_enumdef *e) { + return upb_def_name(upb_enumdef_upcast(e)); +} + bool upb_enumdef_setfullname(upb_enumdef *e, const char *fullname, upb_status *s) { return upb_def_setfullname(upb_enumdef_upcast_mutable(e), fullname, s); @@ -441,27 +509,36 @@ bool upb_enumdef_setfullname(upb_enumdef *e, const char *fullname, bool upb_enumdef_addval(upb_enumdef *e, const char *name, int32_t num, upb_status *status) { + char *name2; + if (!upb_isident(name, strlen(name), false, status)) { return false; } + if (upb_enumdef_ntoiz(e, name, NULL)) { upb_status_seterrf(status, "name '%s' is already defined", name); return false; } + if (!upb_strtable_insert(&e->ntoi, name, upb_value_int32(num))) { upb_status_seterrmsg(status, "out of memory"); return false; } - if (!upb_inttable_lookup(&e->iton, num, NULL) && - !upb_inttable_insert(&e->iton, num, upb_value_cstr(upb_strdup(name)))) { - upb_status_seterrmsg(status, "out of memory"); - upb_strtable_remove(&e->ntoi, name, NULL); - return false; + + if (!upb_inttable_lookup(&e->iton, num, NULL)) { + name2 = upb_gstrdup(name); + if (!name2 || !upb_inttable_insert(&e->iton, num, upb_value_cstr(name2))) { + upb_status_seterrmsg(status, "out of memory"); + upb_strtable_remove(&e->ntoi, name, NULL); + return false; + } } + if (upb_enumdef_numvals(e) == 1) { bool ok = upb_enumdef_setdefault(e, num, NULL); UPB_ASSERT_VAR(ok, ok); } + return true; } @@ -537,7 +614,7 @@ static void visitfield(const upb_refcounted *r, upb_refcounted_visit *visit, visit(r, upb_msgdef_upcast2(upb_fielddef_containingtype(f)), closure); } if (upb_fielddef_containingoneof(f)) { - visit(r, upb_oneofdef_upcast2(upb_fielddef_containingoneof(f)), closure); + visit(r, upb_oneofdef_upcast(upb_fielddef_containingoneof(f)), closure); } if (upb_fielddef_subdef(f)) { visit(r, upb_def_upcast(upb_fielddef_subdef(f)), closure); @@ -548,9 +625,9 @@ static void freefield(upb_refcounted *r) { upb_fielddef *f = (upb_fielddef*)r; upb_fielddef_uninit_default(f); if (f->subdef_is_symbolic) - free(f->sub.name); + upb_gfree(f->sub.name); upb_def_uninit(upb_fielddef_upcast_mutable(f)); - free(f); + upb_gfree(f); } static const char *enumdefaultstr(const upb_fielddef *f) { @@ -606,12 +683,14 @@ static bool enumdefaultint32(const upb_fielddef *f, int32_t *val) { return false; } +const struct upb_refcounted_vtbl upb_fielddef_vtbl = {visitfield, freefield}; + upb_fielddef *upb_fielddef_new(const void *o) { - static const struct upb_refcounted_vtbl vtbl = {visitfield, freefield}; - upb_fielddef *f = malloc(sizeof(*f)); + upb_fielddef *f = upb_gmalloc(sizeof(*f)); if (!f) return NULL; - if (!upb_def_init(upb_fielddef_upcast_mutable(f), UPB_DEF_FIELD, &vtbl, o)) { - free(f); + if (!upb_def_init(upb_fielddef_upcast_mutable(f), UPB_DEF_FIELD, + &upb_fielddef_vtbl, o)) { + upb_gfree(f); return NULL; } f->msg.def = NULL; @@ -662,7 +741,7 @@ upb_fielddef *upb_fielddef_dup(const upb_fielddef *f, const void *owner) { srcname = f->sub.def ? upb_def_fullname(f->sub.def) : NULL; } if (srcname) { - char *newname = malloc(strlen(f->sub.def->fullname) + 2); + char *newname = upb_gmalloc(strlen(f->sub.def->fullname) + 2); if (!newname) { upb_fielddef_unref(newf, owner); return NULL; @@ -670,7 +749,7 @@ upb_fielddef *upb_fielddef_dup(const upb_fielddef *f, const void *owner) { strcpy(newname, "."); strcat(newname, f->sub.def->fullname); upb_fielddef_setsubdefname(newf, newname, NULL); - free(newname); + upb_gfree(newname); } return newf; @@ -777,11 +856,12 @@ const char *upb_fielddef_containingtypename(upb_fielddef *f) { } static void release_containingtype(upb_fielddef *f) { - if (f->msg_is_symbolic) free(f->msg.name); + if (f->msg_is_symbolic) upb_gfree(f->msg.name); } bool upb_fielddef_setcontainingtypename(upb_fielddef *f, const char *name, upb_status *s) { + char *name_copy; assert(!upb_fielddef_isfrozen(f)); if (upb_fielddef_containingtype(f)) { upb_status_seterrmsg(s, "field has already been added to a message."); @@ -789,8 +869,15 @@ bool upb_fielddef_setcontainingtypename(upb_fielddef *f, const char *name, } /* TODO: validate name (upb_isident() doesn't quite work atm because this name * may have a leading "."). */ + + name_copy = upb_gstrdup(name); + if (!name_copy) { + upb_upberr_setoom(s); + return false; + } + release_containingtype(f); - f->msg.name = upb_strdup(name); + f->msg.name = name_copy; f->msg_is_symbolic = true; return true; } @@ -1191,7 +1278,7 @@ static bool upb_subdef_typecheck(upb_fielddef *f, const upb_def *subdef, static void release_subdef(upb_fielddef *f) { if (f->subdef_is_symbolic) { - free(f->sub.name); + upb_gfree(f->sub.name); } else if (f->sub.def) { upb_unref2(f->sub.def, f); } @@ -1221,15 +1308,23 @@ bool upb_fielddef_setenumsubdef(upb_fielddef *f, const upb_enumdef *subdef, bool upb_fielddef_setsubdefname(upb_fielddef *f, const char *name, upb_status *s) { + char *name_copy; assert(!upb_fielddef_isfrozen(f)); if (!upb_fielddef_hassubdef(f)) { upb_status_seterrmsg(s, "field type does not accept a subdef"); return false; } + + name_copy = upb_gstrdup(name); + if (!name_copy) { + upb_upberr_setoom(s); + return false; + } + /* TODO: validate name (upb_isident() doesn't quite work atm because this name * may have a leading "."). */ release_subdef(f); - f->sub.name = upb_strdup(name); + f->sub.name = name_copy; f->subdef_is_symbolic = true; return true; } @@ -1263,7 +1358,7 @@ bool upb_fielddef_haspresence(const upb_fielddef *f) { /* Primitive field: return true unless there is a message that specifies * presence should not exist. */ if (f->msg_is_symbolic || !f->msg.def) return true; - return f->msg.def->primitives_have_presence; + return f->msg.def->syntax == UPB_SYNTAX_PROTO2; } bool upb_fielddef_hassubdef(const upb_fielddef *f) { @@ -1299,38 +1394,39 @@ static void visitmsg(const upb_refcounted *r, upb_refcounted_visit *visit, !upb_msg_oneof_done(&o); upb_msg_oneof_next(&o)) { upb_oneofdef *f = upb_msg_iter_oneof(&o); - visit(r, upb_oneofdef_upcast2(f), closure); + visit(r, upb_oneofdef_upcast(f), closure); } } static void freemsg(upb_refcounted *r) { upb_msgdef *m = (upb_msgdef*)r; - upb_strtable_uninit(&m->ntoo); upb_strtable_uninit(&m->ntof); upb_inttable_uninit(&m->itof); upb_def_uninit(upb_msgdef_upcast_mutable(m)); - free(m); + upb_gfree(m); } +const struct upb_refcounted_vtbl upb_msgdef_vtbl = {visitmsg, freemsg}; + upb_msgdef *upb_msgdef_new(const void *owner) { - static const struct upb_refcounted_vtbl vtbl = {visitmsg, freemsg}; - upb_msgdef *m = malloc(sizeof(*m)); + upb_msgdef *m = upb_gmalloc(sizeof(*m)); if (!m) return NULL; - if (!upb_def_init(upb_msgdef_upcast_mutable(m), UPB_DEF_MSG, &vtbl, owner)) + + if (!upb_def_init(upb_msgdef_upcast_mutable(m), UPB_DEF_MSG, &upb_msgdef_vtbl, + owner)) { goto err2; - if (!upb_inttable_init(&m->itof, UPB_CTYPE_PTR)) goto err3; - if (!upb_strtable_init(&m->ntof, UPB_CTYPE_PTR)) goto err2; - if (!upb_strtable_init(&m->ntoo, UPB_CTYPE_PTR)) goto err1; + } + + if (!upb_inttable_init(&m->itof, UPB_CTYPE_PTR)) goto err2; + if (!upb_strtable_init(&m->ntof, UPB_CTYPE_PTR)) goto err1; m->map_entry = false; - m->primitives_have_presence = true; + m->syntax = UPB_SYNTAX_PROTO2; return m; err1: - upb_strtable_uninit(&m->ntof); -err2: upb_inttable_uninit(&m->itof); -err3: - free(m); +err2: + upb_gfree(m); return NULL; } @@ -1345,7 +1441,7 @@ upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner) { upb_def_fullname(upb_msgdef_upcast(m)), NULL); newm->map_entry = m->map_entry; - newm->primitives_have_presence = m->primitives_have_presence; + newm->syntax = m->syntax; UPB_ASSERT_VAR(ok, ok); for(upb_msg_field_begin(&i, m); !upb_msg_field_done(&i); @@ -1379,11 +1475,28 @@ const char *upb_msgdef_fullname(const upb_msgdef *m) { return upb_def_fullname(upb_msgdef_upcast(m)); } +const char *upb_msgdef_name(const upb_msgdef *m) { + return upb_def_name(upb_msgdef_upcast(m)); +} + bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, upb_status *s) { return upb_def_setfullname(upb_msgdef_upcast_mutable(m), fullname, s); } +bool upb_msgdef_setsyntax(upb_msgdef *m, upb_syntax_t syntax) { + if (syntax != UPB_SYNTAX_PROTO2 && syntax != UPB_SYNTAX_PROTO3) { + return false; + } + + m->syntax = syntax; + return true; +} + +upb_syntax_t upb_msgdef_syntax(const upb_msgdef *m) { + return m->syntax; +} + /* Helper: check that the field |f| is safe to add to msgdef |m|. Set an error * on status |s| and return false if not. */ static bool check_field_add(const upb_msgdef *m, const upb_fielddef *f, @@ -1394,9 +1507,11 @@ static bool check_field_add(const upb_msgdef *m, const upb_fielddef *f, } else if (upb_fielddef_name(f) == NULL || upb_fielddef_number(f) == 0) { upb_status_seterrmsg(s, "field name or number were not set"); return false; - } else if (upb_msgdef_ntofz(m, upb_fielddef_name(f)) || - upb_msgdef_itof(m, upb_fielddef_number(f))) { - upb_status_seterrmsg(s, "duplicate field name or number for field"); + } else if (upb_msgdef_itof(m, upb_fielddef_number(f))) { + upb_status_seterrmsg(s, "duplicate field number"); + return false; + } else if (upb_strtable_lookup(&m->ntof, upb_fielddef_name(f), NULL)) { + upb_status_seterrmsg(s, "name conflicts with existing field or oneof"); return false; } return true; @@ -1457,8 +1572,8 @@ bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor, } else if (upb_oneofdef_name(o) == NULL) { upb_status_seterrmsg(s, "oneofdef name was not set"); return false; - } else if (upb_msgdef_ntooz(m, upb_oneofdef_name(o))) { - upb_status_seterrmsg(s, "duplicate oneof name"); + } else if (upb_strtable_lookup(&m->ntof, upb_oneofdef_name(o), NULL)) { + upb_status_seterrmsg(s, "name conflicts with existing field or oneof"); return false; } @@ -1475,7 +1590,7 @@ bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor, /* Add oneof itself first. */ o->parent = m; - upb_strtable_insert(&m->ntoo, upb_oneofdef_name(o), upb_value_ptr(o)); + upb_strtable_insert(&m->ntof, upb_oneofdef_name(o), upb_value_ptr(o)); upb_ref2(o, m); upb_ref2(m, o); @@ -1490,11 +1605,6 @@ bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor, return true; } -void upb_msgdef_setprimitiveshavepresence(upb_msgdef *m, bool have_presence) { - assert(!upb_msgdef_isfrozen(m)); - m->primitives_have_presence = have_presence; -} - const upb_fielddef *upb_msgdef_itof(const upb_msgdef *m, uint32_t i) { upb_value val; return upb_inttable_lookup32(&m->itof, i, &val) ? @@ -1504,23 +1614,47 @@ const upb_fielddef *upb_msgdef_itof(const upb_msgdef *m, uint32_t i) { const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name, size_t len) { upb_value val; - return upb_strtable_lookup2(&m->ntof, name, len, &val) ? - upb_value_getptr(val) : NULL; + + if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) { + return NULL; + } + + return upb_trygetfield(upb_value_getptr(val)); } const upb_oneofdef *upb_msgdef_ntoo(const upb_msgdef *m, const char *name, size_t len) { upb_value val; - return upb_strtable_lookup2(&m->ntoo, name, len, &val) ? - upb_value_getptr(val) : NULL; + + if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) { + return NULL; + } + + return upb_trygetoneof(upb_value_getptr(val)); +} + +bool upb_msgdef_lookupname(const upb_msgdef *m, const char *name, size_t len, + const upb_fielddef **f, const upb_oneofdef **o) { + upb_value val; + + if (!upb_strtable_lookup2(&m->ntof, name, len, &val)) { + return false; + } + + *o = upb_trygetoneof(upb_value_getptr(val)); + *f = upb_trygetfield(upb_value_getptr(val)); + assert((*o != NULL) ^ (*f != NULL)); /* Exactly one of the two should be set. */ + return true; } int upb_msgdef_numfields(const upb_msgdef *m) { - return upb_strtable_count(&m->ntof); + /* The number table contains only fields. */ + return upb_inttable_count(&m->itof); } int upb_msgdef_numoneofs(const upb_msgdef *m) { - return upb_strtable_count(&m->ntoo); + /* The name table includes oneofs, and the number table does not. */ + return upb_strtable_count(&m->ntof) - upb_inttable_count(&m->itof); } void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry) { @@ -1551,10 +1685,21 @@ void upb_msg_field_iter_setdone(upb_msg_field_iter *iter) { } void upb_msg_oneof_begin(upb_msg_oneof_iter *iter, const upb_msgdef *m) { - upb_strtable_begin(iter, &m->ntoo); + upb_strtable_begin(iter, &m->ntof); + /* We need to skip past any initial fields. */ + while (!upb_strtable_done(iter) && + !upb_isoneof(upb_value_getptr(upb_strtable_iter_value(iter)))) { + upb_strtable_next(iter); + } } -void upb_msg_oneof_next(upb_msg_oneof_iter *iter) { upb_strtable_next(iter); } +void upb_msg_oneof_next(upb_msg_oneof_iter *iter) { + /* We need to skip past fields to return only oneofs. */ + do { + upb_strtable_next(iter); + } while (!upb_strtable_done(iter) && + !upb_isoneof(upb_value_getptr(upb_strtable_iter_value(iter)))); +} bool upb_msg_oneof_done(const upb_msg_oneof_iter *iter) { return upb_strtable_done(iter); @@ -1587,26 +1732,36 @@ static void freeoneof(upb_refcounted *r) { upb_oneofdef *o = (upb_oneofdef*)r; upb_strtable_uninit(&o->ntof); upb_inttable_uninit(&o->itof); - upb_def_uninit(upb_oneofdef_upcast_mutable(o)); - free(o); + upb_gfree((void*)o->name); + upb_gfree(o); } +const struct upb_refcounted_vtbl upb_oneofdef_vtbl = {visitoneof, freeoneof}; + upb_oneofdef *upb_oneofdef_new(const void *owner) { - static const struct upb_refcounted_vtbl vtbl = {visitoneof, freeoneof}; - upb_oneofdef *o = malloc(sizeof(*o)); + upb_oneofdef *o = upb_gmalloc(sizeof(*o)); + + if (!o) { + return NULL; + } + o->parent = NULL; - if (!o) return NULL; - if (!upb_def_init(upb_oneofdef_upcast_mutable(o), UPB_DEF_ONEOF, &vtbl, - owner)) + o->name = NULL; + + if (!upb_refcounted_init(upb_oneofdef_upcast_mutable(o), &upb_oneofdef_vtbl, + owner)) { goto err2; + } + if (!upb_inttable_init(&o->itof, UPB_CTYPE_PTR)) goto err2; if (!upb_strtable_init(&o->ntof, UPB_CTYPE_PTR)) goto err1; + return o; err1: upb_inttable_uninit(&o->itof); err2: - free(o); + upb_gfree(o); return NULL; } @@ -1615,8 +1770,7 @@ upb_oneofdef *upb_oneofdef_dup(const upb_oneofdef *o, const void *owner) { upb_oneof_iter i; upb_oneofdef *newo = upb_oneofdef_new(owner); if (!newo) return NULL; - ok = upb_def_setfullname(upb_oneofdef_upcast_mutable(newo), - upb_def_fullname(upb_oneofdef_upcast(o)), NULL); + ok = upb_oneofdef_setname(newo, upb_oneofdef_name(o), NULL); UPB_ASSERT_VAR(ok, ok); for (upb_oneof_begin(&i, o); !upb_oneof_done(&i); upb_oneof_next(&i)) { upb_fielddef *f = upb_fielddef_dup(upb_oneof_iter_field(&i), &f); @@ -1628,17 +1782,28 @@ upb_oneofdef *upb_oneofdef_dup(const upb_oneofdef *o, const void *owner) { return newo; } -const char *upb_oneofdef_name(const upb_oneofdef *o) { - return upb_def_fullname(upb_oneofdef_upcast(o)); -} +const char *upb_oneofdef_name(const upb_oneofdef *o) { return o->name; } -bool upb_oneofdef_setname(upb_oneofdef *o, const char *fullname, - upb_status *s) { +bool upb_oneofdef_setname(upb_oneofdef *o, const char *name, upb_status *s) { + assert(!upb_oneofdef_isfrozen(o)); if (upb_oneofdef_containingtype(o)) { upb_status_seterrmsg(s, "oneof already added to a message"); return false; } - return upb_def_setfullname(upb_oneofdef_upcast_mutable(o), fullname, s); + + if (!upb_isident(name, strlen(name), true, s)) { + return false; + } + + name = upb_gstrdup(name); + if (!name) { + upb_status_seterrmsg(s, "One of memory"); + return false; + } + + upb_gfree((void*)o->name); + o->name = name; + return true; } const upb_msgdef *upb_oneofdef_containingtype(const upb_oneofdef *o) { @@ -1757,287 +1922,208 @@ void upb_oneof_iter_setdone(upb_oneof_iter *iter) { upb_inttable_iter_setdone(iter); } +/* upb_filedef ****************************************************************/ -#include -#include -#include +static void visitfiledef(const upb_refcounted *r, upb_refcounted_visit *visit, + void *closure) { + const upb_filedef *f = (const upb_filedef*)r; + size_t i; -typedef struct cleanup_ent { - upb_cleanup_func *cleanup; - void *ud; - struct cleanup_ent *next; -} cleanup_ent; - -static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, size_t size); - -/* Default allocator **********************************************************/ - -/* Just use realloc, keeping all allocated blocks in a linked list to destroy at - * the end. */ - -typedef struct mem_block { - /* List is doubly-linked, because in cases where realloc() moves an existing - * block, we need to be able to remove the old pointer from the list - * efficiently. */ - struct mem_block *prev, *next; -#ifndef NDEBUG - size_t size; /* Doesn't include mem_block structure. */ -#endif -} mem_block; - -typedef struct { - mem_block *head; -} default_alloc_ud; - -static void *default_alloc(void *_ud, void *ptr, size_t oldsize, size_t size) { - default_alloc_ud *ud = _ud; - mem_block *from, *block; - void *ret; - UPB_UNUSED(oldsize); - - from = ptr ? (void*)((char*)ptr - sizeof(mem_block)) : NULL; - -#ifndef NDEBUG - if (from) { - assert(oldsize <= from->size); + for(i = 0; i < upb_filedef_defcount(f); i++) { + visit(r, upb_def_upcast(upb_filedef_def(f, i)), closure); } -#endif +} - /* TODO(haberman): we probably need to provide even better alignment here, - * like 16-byte alignment of the returned data pointer. */ - block = realloc(from, size + sizeof(mem_block)); - if (!block) return NULL; - ret = (char*)block + sizeof(*block); +static void freefiledef(upb_refcounted *r) { + upb_filedef *f = (upb_filedef*)r; + size_t i; -#ifndef NDEBUG - block->size = size; -#endif + for(i = 0; i < upb_filedef_depcount(f); i++) { + upb_filedef_unref(upb_filedef_dep(f, i), f); + } - if (from) { - if (block != from) { - /* The block was moved, so pointers in next and prev blocks must be - * updated to its new location. */ - if (block->next) block->next->prev = block; - if (block->prev) block->prev->next = block; - if (ud->head == from) ud->head = block; - } + upb_inttable_uninit(&f->defs); + upb_inttable_uninit(&f->deps); + upb_gfree((void*)f->name); + upb_gfree((void*)f->package); + upb_gfree(f); +} + +const struct upb_refcounted_vtbl upb_filedef_vtbl = {visitfiledef, freefiledef}; + +upb_filedef *upb_filedef_new(const void *owner) { + upb_filedef *f = upb_gmalloc(sizeof(*f)); + + if (!f) { + return NULL; + } + + f->package = NULL; + f->name = NULL; + f->syntax = UPB_SYNTAX_PROTO2; + + if (!upb_refcounted_init(upb_filedef_upcast_mutable(f), &upb_filedef_vtbl, + owner)) { + goto err; + } + + if (!upb_inttable_init(&f->defs, UPB_CTYPE_CONSTPTR)) { + goto err; + } + + if (!upb_inttable_init(&f->deps, UPB_CTYPE_CONSTPTR)) { + goto err2; + } + + return f; + + +err2: + upb_inttable_uninit(&f->defs); + +err: + upb_gfree(f); + return NULL; +} + +const char *upb_filedef_name(const upb_filedef *f) { + return f->name; +} + +const char *upb_filedef_package(const upb_filedef *f) { + return f->package; +} + +upb_syntax_t upb_filedef_syntax(const upb_filedef *f) { + return f->syntax; +} + +size_t upb_filedef_defcount(const upb_filedef *f) { + return upb_inttable_count(&f->defs); +} + +size_t upb_filedef_depcount(const upb_filedef *f) { + return upb_inttable_count(&f->deps); +} + +const upb_def *upb_filedef_def(const upb_filedef *f, size_t i) { + upb_value v; + + if (upb_inttable_lookup32(&f->defs, i, &v)) { + return upb_value_getconstptr(v); } else { - /* Insert at head of linked list. */ - block->prev = NULL; - block->next = ud->head; - if (block->next) block->next->prev = block; - ud->head = block; - } - - return ret; -} - -static void default_alloc_cleanup(void *_ud) { - default_alloc_ud *ud = _ud; - mem_block *block = ud->head; - - while (block) { - void *to_free = block; - block = block->next; - free(to_free); + return NULL; } } +const upb_filedef *upb_filedef_dep(const upb_filedef *f, size_t i) { + upb_value v; -/* Standard error functions ***************************************************/ - -static bool default_err(void *ud, const upb_status *status) { - UPB_UNUSED(ud); - UPB_UNUSED(status); - return false; -} - -static bool write_err_to(void *ud, const upb_status *status) { - upb_status *copy_to = ud; - upb_status_copy(copy_to, status); - return false; -} - - -/* upb_env ********************************************************************/ - -void upb_env_init(upb_env *e) { - default_alloc_ud *ud = (default_alloc_ud*)&e->default_alloc_ud; - e->ok_ = true; - e->bytes_allocated = 0; - e->cleanup_head = NULL; - - ud->head = NULL; - - /* Set default functions. */ - upb_env_setallocfunc(e, default_alloc, ud); - upb_env_seterrorfunc(e, default_err, NULL); -} - -void upb_env_uninit(upb_env *e) { - cleanup_ent *ent = e->cleanup_head; - - while (ent) { - ent->cleanup(ent->ud); - ent = ent->next; - } - - /* Must do this after running cleanup functions, because this will delete - the memory we store our cleanup entries in! */ - if (e->alloc == default_alloc) { - default_alloc_cleanup(e->alloc_ud); + if (upb_inttable_lookup32(&f->deps, i, &v)) { + return upb_value_getconstptr(v); + } else { + return NULL; } } -UPB_FORCEINLINE void upb_env_setallocfunc(upb_env *e, upb_alloc_func *alloc, - void *ud) { - e->alloc = alloc; - e->alloc_ud = ud; +bool upb_filedef_setname(upb_filedef *f, const char *name, upb_status *s) { + name = upb_gstrdup(name); + if (!name) { + upb_upberr_setoom(s); + return false; + } + upb_gfree((void*)f->name); + f->name = name; + return true; } -UPB_FORCEINLINE void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, - void *ud) { - e->err = func; - e->err_ud = ud; +bool upb_filedef_setpackage(upb_filedef *f, const char *package, + upb_status *s) { + if (!upb_isident(package, strlen(package), true, s)) return false; + package = upb_gstrdup(package); + if (!package) { + upb_upberr_setoom(s); + return false; + } + upb_gfree((void*)f->package); + f->package = package; + return true; } -void upb_env_reporterrorsto(upb_env *e, upb_status *status) { - e->err = write_err_to; - e->err_ud = status; -} +bool upb_filedef_setsyntax(upb_filedef *f, upb_syntax_t syntax, + upb_status *s) { + UPB_UNUSED(s); + if (syntax != UPB_SYNTAX_PROTO2 && + syntax != UPB_SYNTAX_PROTO3) { + upb_status_seterrmsg(s, "Unknown syntax value."); + return false; + } + f->syntax = syntax; -bool upb_env_ok(const upb_env *e) { - return e->ok_; -} + { + /* Set all messages in this file to match. */ + size_t i; + for (i = 0; i < upb_filedef_defcount(f); i++) { + /* Casting const away is safe since all defs in mutable filedef must + * also be mutable. */ + upb_def *def = (upb_def*)upb_filedef_def(f, i); -bool upb_env_reporterror(upb_env *e, const upb_status *status) { - e->ok_ = false; - return e->err(e->err_ud, status); -} - -bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) { - cleanup_ent *ent = upb_env_malloc(e, sizeof(cleanup_ent)); - if (!ent) return false; - - ent->cleanup = func; - ent->ud = ud; - ent->next = e->cleanup_head; - e->cleanup_head = ent; + upb_msgdef *m = upb_dyncast_msgdef_mutable(def); + if (m) { + m->syntax = syntax; + } + } + } return true; } -void *upb_env_malloc(upb_env *e, size_t size) { - e->bytes_allocated += size; - if (e->alloc == seeded_alloc) { - /* This is equivalent to the next branch, but allows inlining for a - * measurable perf benefit. */ - return seeded_alloc(e->alloc_ud, NULL, 0, size); - } else { - return e->alloc(e->alloc_ud, NULL, 0, size); +bool upb_filedef_adddef(upb_filedef *f, upb_def *def, const void *ref_donor, + upb_status *s) { + if (def->file) { + upb_status_seterrmsg(s, "Def is already part of another filedef."); + return false; } -} -void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) { - char *ret; - assert(oldsize <= size); - ret = e->alloc(e->alloc_ud, ptr, oldsize, size); - -#ifndef NDEBUG - /* Overwrite non-preserved memory to ensure callers are passing the oldsize - * that they truly require. */ - memset(ret + oldsize, 0xff, size - oldsize); -#endif - - return ret; -} - -size_t upb_env_bytesallocated(const upb_env *e) { - return e->bytes_allocated; -} - - -/* upb_seededalloc ************************************************************/ - -/* Be conservative and choose 16 in case anyone is using SSE. */ -static const size_t maxalign = 16; - -static size_t align_up(size_t size) { - return ((size + maxalign - 1) / maxalign) * maxalign; -} - -UPB_FORCEINLINE static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, - size_t size) { - upb_seededalloc *a = ud; - - size = align_up(size); - - assert(a->mem_limit >= a->mem_ptr); - - if (oldsize == 0 && size <= (size_t)(a->mem_limit - a->mem_ptr)) { - /* Fast path: we can satisfy from the initial allocation. */ - void *ret = a->mem_ptr; - a->mem_ptr += size; - return ret; - } else { - char *chptr = ptr; - /* Slow path: fallback to other allocator. */ - a->need_cleanup = true; - /* Is `ptr` part of the user-provided initial block? Don't pass it to the - * default allocator if so; otherwise, it may try to realloc() the block. */ - if (chptr >= a->mem_base && chptr < a->mem_limit) { - void *ret; - assert(chptr + oldsize <= a->mem_limit); - ret = a->alloc(a->alloc_ud, NULL, 0, size); - if (ret) memcpy(ret, ptr, oldsize); - return ret; - } else { - return a->alloc(a->alloc_ud, ptr, oldsize, size); + if (upb_inttable_push(&f->defs, upb_value_constptr(def))) { + def->file = f; + upb_ref2(def, f); + if (ref_donor) upb_def_unref(def, ref_donor); + if (def->type == UPB_DEF_MSG) { + upb_downcast_msgdef_mutable(def)->syntax = f->syntax; } + return true; + } else { + upb_upberr_setoom(s); + return false; } } -void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len) { - default_alloc_ud *ud = (default_alloc_ud*)&a->default_alloc_ud; - a->mem_base = mem; - a->mem_ptr = mem; - a->mem_limit = (char*)mem + len; - a->need_cleanup = false; - a->returned_allocfunc = false; - - ud->head = NULL; - - upb_seededalloc_setfallbackalloc(a, default_alloc, ud); -} - -void upb_seededalloc_uninit(upb_seededalloc *a) { - if (a->alloc == default_alloc && a->need_cleanup) { - default_alloc_cleanup(a->alloc_ud); +bool upb_filedef_adddep(upb_filedef *f, const upb_filedef *dep) { + if (upb_inttable_push(&f->deps, upb_value_constptr(dep))) { + /* Regular ref instead of ref2 because files can't form cycles. */ + upb_filedef_ref(dep, f); + return true; + } else { + return false; } } - -UPB_FORCEINLINE void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, - upb_alloc_func *alloc, - void *ud) { - assert(!a->returned_allocfunc); - a->alloc = alloc; - a->alloc_ud = ud; -} - -upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a) { - a->returned_allocfunc = true; - return seeded_alloc; -} /* ** TODO(haberman): it's unclear whether a lot of the consistency checks should ** assert() or return false. */ -#include #include +static void *upb_calloc(size_t size) { + void *mem = upb_gmalloc(size); + if (mem) { + memset(mem, 0, size); + } + return mem; +} /* Defined for the sole purpose of having a unique pointer value for * UPB_NO_CLOSURE. */ @@ -2057,8 +2143,8 @@ static void freehandlers(upb_refcounted *r) { upb_inttable_uninit(&h->cleanup_); upb_msgdef_unref(h->msg, h); - free(h->sub); - free(h); + upb_gfree(h->sub); + upb_gfree(h); } static void visithandlers(const upb_refcounted *r, upb_refcounted_visit *visit, @@ -2308,14 +2394,20 @@ upb_handlers *upb_handlers_new(const upb_msgdef *md, const void *owner) { assert(upb_msgdef_isfrozen(md)); extra = sizeof(upb_handlers_tabent) * (md->selector_count - 1); - h = calloc(sizeof(*h) + extra, 1); + h = upb_calloc(sizeof(*h) + extra); if (!h) return NULL; h->msg = md; upb_msgdef_ref(h->msg, h); upb_status_clear(&h->status_); - h->sub = calloc(md->submsg_field_count, sizeof(*h->sub)); - if (!h->sub) goto oom; + + if (md->submsg_field_count > 0) { + h->sub = upb_calloc(md->submsg_field_count * sizeof(*h->sub)); + if (!h->sub) goto oom; + } else { + h->sub = 0; + } + if (!upb_refcounted_init(upb_handlers_upcast_mutable(h), &vtbl, owner)) goto oom; if (!upb_inttable_init(&h->cleanup_, UPB_CTYPE_FPTR)) goto oom; @@ -2730,7 +2822,6 @@ bool upb_byteshandler_setendstr(upb_byteshandler *h, #include -#include static void freeobj(upb_refcounted *o); @@ -2806,8 +2897,31 @@ void upb_unlock(); /* UPB_DEBUG_REFS mode counts on being able to malloc() memory in some * code-paths that can normally never fail, like upb_refcounted_ref(). Since * we have no way to propagage out-of-memory errors back to the user, and since - * these errors can only occur in UPB_DEBUG_REFS mode, we immediately fail. */ -#define CHECK_OOM(predicate) if (!(predicate)) { assert(predicate); exit(1); } + * these errors can only occur in UPB_DEBUG_REFS mode, we use an allocator that + * immediately aborts on failure (avoiding the global allocator, which might + * inject failures). */ + +#include + +static void *upb_debugrefs_allocfunc(upb_alloc *alloc, void *ptr, + size_t oldsize, size_t size) { + UPB_UNUSED(alloc); + UPB_UNUSED(oldsize); + if (size == 0) { + free(ptr); + return NULL; + } else { + void *ret = realloc(ptr, size); + + if (!ret) { + abort(); + } + + return ret; + } +} + +upb_alloc upb_alloc_debugrefs = {&upb_debugrefs_allocfunc}; typedef struct { int count; /* How many refs there are (duplicates only allowed for ref2). */ @@ -2815,8 +2929,7 @@ typedef struct { } trackedref; static trackedref *trackedref_new(bool is_ref2) { - trackedref *ret = malloc(sizeof(*ret)); - CHECK_OOM(ret); + trackedref *ret = upb_malloc(&upb_alloc_debugrefs, sizeof(*ret)); ret->count = 1; ret->is_ref2 = is_ref2; return ret; @@ -2841,15 +2954,15 @@ static void track(const upb_refcounted *r, const void *owner, bool ref2) { ref->count++; } else { trackedref *ref = trackedref_new(ref2); - bool ok = upb_inttable_insertptr(r->refs, owner, upb_value_ptr(ref)); - CHECK_OOM(ok); + upb_inttable_insertptr2(r->refs, owner, upb_value_ptr(ref), + &upb_alloc_debugrefs); if (ref2) { /* We know this cast is safe when it is a ref2, because it's coming from * another refcounted object. */ const upb_refcounted *from = owner; assert(!upb_inttable_lookupptr(from->ref2s, r, NULL)); - ok = upb_inttable_insertptr(from->ref2s, r, upb_value_ptr(NULL)); - CHECK_OOM(ok); + upb_inttable_insertptr2(from->ref2s, r, upb_value_ptr(NULL), + &upb_alloc_debugrefs); } } upb_unlock(); @@ -2907,7 +3020,6 @@ static void getref2s(const upb_refcounted *owner, upb_inttable *tab) { upb_value v; upb_value count; trackedref *ref; - bool ok; bool found; upb_refcounted *to = (upb_refcounted*)upb_inttable_iter_key(&i); @@ -2918,8 +3030,7 @@ static void getref2s(const upb_refcounted *owner, upb_inttable *tab) { ref = upb_value_getptr(v); count = upb_value_int32(ref->count); - ok = upb_inttable_insertptr(tab, to, count); - CHECK_OOM(ok); + upb_inttable_insertptr2(tab, to, count, &upb_alloc_debugrefs); } upb_unlock(); } @@ -2945,21 +3056,19 @@ static void visit_check(const upb_refcounted *obj, const upb_refcounted *subobj, assert(removed); newcount = upb_value_getint32(v) - 1; if (newcount > 0) { - upb_inttable_insert(ref2, (uintptr_t)subobj, upb_value_int32(newcount)); + upb_inttable_insert2(ref2, (uintptr_t)subobj, upb_value_int32(newcount), + &upb_alloc_debugrefs); } } static void visit(const upb_refcounted *r, upb_refcounted_visit *v, void *closure) { - bool ok; - /* In DEBUG_REFS mode we know what existing ref2 refs there are, so we know * exactly the set of nodes that visit() should visit. So we verify visit()'s * correctness here. */ check_state state; state.obj = r; - ok = upb_inttable_init(&state.ref2, UPB_CTYPE_INT32); - CHECK_OOM(ok); + upb_inttable_init2(&state.ref2, UPB_CTYPE_INT32, &upb_alloc_debugrefs); getref2s(r, &state.ref2); /* This should visit any children in the ref2 table. */ @@ -2967,32 +3076,22 @@ static void visit(const upb_refcounted *r, upb_refcounted_visit *v, /* This assertion will fail if the visit() function missed any children. */ assert(upb_inttable_count(&state.ref2) == 0); - upb_inttable_uninit(&state.ref2); + upb_inttable_uninit2(&state.ref2, &upb_alloc_debugrefs); if (r->vtbl->visit) r->vtbl->visit(r, v, closure); } -static bool trackinit(upb_refcounted *r) { - r->refs = malloc(sizeof(*r->refs)); - r->ref2s = malloc(sizeof(*r->ref2s)); - if (!r->refs || !r->ref2s) goto err1; - - if (!upb_inttable_init(r->refs, UPB_CTYPE_PTR)) goto err1; - if (!upb_inttable_init(r->ref2s, UPB_CTYPE_PTR)) goto err2; - return true; - -err2: - upb_inttable_uninit(r->refs); -err1: - free(r->refs); - free(r->ref2s); - return false; +static void trackinit(upb_refcounted *r) { + r->refs = upb_malloc(&upb_alloc_debugrefs, sizeof(*r->refs)); + r->ref2s = upb_malloc(&upb_alloc_debugrefs, sizeof(*r->ref2s)); + upb_inttable_init2(r->refs, UPB_CTYPE_PTR, &upb_alloc_debugrefs); + upb_inttable_init2(r->ref2s, UPB_CTYPE_PTR, &upb_alloc_debugrefs); } static void trackfree(const upb_refcounted *r) { - upb_inttable_uninit(r->refs); - upb_inttable_uninit(r->ref2s); - free(r->refs); - free(r->ref2s); + upb_inttable_uninit2(r->refs, &upb_alloc_debugrefs); + upb_inttable_uninit2(r->ref2s, &upb_alloc_debugrefs); + upb_free(&upb_alloc_debugrefs, r->refs); + upb_free(&upb_alloc_debugrefs, r->ref2s); } #else @@ -3015,9 +3114,8 @@ static void checkref(const upb_refcounted *r, const void *owner, bool ref2) { UPB_UNUSED(ref2); } -static bool trackinit(upb_refcounted *r) { +static void trackinit(upb_refcounted *r) { UPB_UNUSED(r); - return true; } static void trackfree(const upb_refcounted *r) { @@ -3127,12 +3225,12 @@ static upb_refcounted *pop(tarjan *t) { } static void tarjan_newgroup(tarjan *t) { - uint32_t *group = malloc(sizeof(*group)); + uint32_t *group = upb_gmalloc(sizeof(*group)); if (!group) oom(t); /* Push group and empty group leader (we'll fill in leader later). */ if (!upb_inttable_push(&t->groups, upb_value_ptr(group)) || !upb_inttable_push(&t->groups, upb_value_ptr(NULL))) { - free(group); + upb_gfree(group); oom(t); } *group = 0; @@ -3307,7 +3405,7 @@ static bool freeze(upb_refcounted *const*roots, int n, upb_status *s, if (obj == move) { /* Removing the last object from a group. */ assert(*obj->group == obj->individual_count); - free(obj->group); + upb_gfree(obj->group); } else { obj->next = move->next; /* This may decrease to zero; we'll collect GRAY objects (if any) that @@ -3363,7 +3461,7 @@ static bool freeze(upb_refcounted *const*roots, int n, upb_status *s, /* We eagerly free() the group's count (since we can't easily determine * the group's remaining size it's the easiest way to ensure it gets * done). */ - free(obj->group); + upb_gfree(obj->group); /* Visit to release ref2's (done in a separate pass since release_ref2 * depends on o->group being unmodified so it can test merged()). */ @@ -3383,7 +3481,7 @@ err4: if (!ret) { upb_inttable_begin(&iter, &t.groups); for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) - free(upb_value_getptr(upb_inttable_iter_value(&iter))); + upb_gfree(upb_value_getptr(upb_inttable_iter_value(&iter))); } upb_inttable_uninit(&t.groups); err3: @@ -3407,7 +3505,7 @@ static void merge(upb_refcounted *r, upb_refcounted *from) { if (merged(r, from)) return; *r->group += *from->group; - free(from->group); + upb_gfree(from->group); base = from; /* Set all refcount pointers in the "from" chain to the merged refcount. @@ -3441,7 +3539,7 @@ static void unref(const upb_refcounted *r) { if (unrefgroup(r->group)) { const upb_refcounted *o; - free(r->group); + upb_gfree(r->group); /* In two passes, since release_ref2 needs a guarantee that any subobjs * are alive. */ @@ -3485,13 +3583,10 @@ bool upb_refcounted_init(upb_refcounted *r, r->vtbl = vtbl; r->individual_count = 0; r->is_frozen = false; - r->group = malloc(sizeof(*r->group)); + r->group = upb_gmalloc(sizeof(*r->group)); if (!r->group) return false; *r->group = 0; - if (!trackinit(r)) { - free(r->group); - return false; - } + trackinit(r); upb_refcounted_ref(r, owner); return true; } @@ -3550,15 +3645,16 @@ void upb_refcounted_checkref(const upb_refcounted *r, const void *owner) { bool upb_refcounted_freeze(upb_refcounted *const*roots, int n, upb_status *s, int maxdepth) { int i; + bool ret; for (i = 0; i < n; i++) { assert(!roots[i]->is_frozen); } - return freeze(roots, n, s, maxdepth); + ret = freeze(roots, n, s, maxdepth); + assert(!s || ret == upb_ok(s)); + return ret; } -#include - /* Fallback implementation if the shim is not specialized by the JIT. */ #define SHIM_WRITER(type, ctype) \ bool upb_shim_set ## type (void *c, const void *hd, ctype val) { \ @@ -3584,14 +3680,14 @@ bool upb_shim_set(upb_handlers *h, const upb_fielddef *f, size_t offset, upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; bool ok; - upb_shim_data *d = malloc(sizeof(*d)); + upb_shim_data *d = upb_gmalloc(sizeof(*d)); if (!d) return false; d->offset = offset; d->hasbit = hasbit; upb_handlerattr_sethandlerdata(&attr, d); upb_handlerattr_setalwaysok(&attr, true); - upb_handlers_addcleanup(h, d, free); + upb_handlers_addcleanup(h, d, upb_gfree); #define TYPE(u, l) \ case UPB_TYPE_##u: \ @@ -3642,7 +3738,6 @@ const upb_shim_data *upb_shim_getdata(const upb_handlers *h, upb_selector_t s, } -#include #include static void upb_symtab_free(upb_refcounted *r) { @@ -3654,13 +3749,17 @@ static void upb_symtab_free(upb_refcounted *r) { upb_def_unref(def, s); } upb_strtable_uninit(&s->symtab); - free(s); + upb_gfree(s); } - upb_symtab *upb_symtab_new(const void *owner) { static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_symtab_free}; - upb_symtab *s = malloc(sizeof(*s)); + + upb_symtab *s = upb_gmalloc(sizeof(*s)); + if (!s) { + return NULL; + } + upb_refcounted_init(upb_symtab_upcast_mutable(s), &vtbl, owner); upb_strtable_init(&s->symtab, UPB_CTYPE_PTR); return s; @@ -3834,14 +3933,23 @@ oom: /* TODO(haberman): we need a lot more testing of error conditions. * The came_from_user stuff in particular is not tested. */ -bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, - upb_status *status) { - int i; +static bool symtab_add(upb_symtab *s, upb_def *const*defs, size_t n, + void *ref_donor, upb_refcounted *freeze_also, + upb_status *status) { + size_t i; + size_t add_n; + size_t freeze_n; upb_strtable_iter iter; + upb_refcounted **add_objs = NULL; upb_def **add_defs = NULL; + size_t add_objs_size; upb_strtable addtab; upb_inttable seen; + if (n == 0 && !freeze_also) { + return true; + } + assert(!upb_symtab_isfrozen(s)); if (!upb_strtable_init(&addtab, UPB_CTYPE_PTR)) { upb_status_seterrmsg(status, "out of memory"); @@ -3983,15 +4091,38 @@ bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, } } - /* We need an array of the defs in addtab, for passing to upb_def_freeze. */ - add_defs = malloc(sizeof(void*) * upb_strtable_count(&addtab)); - if (add_defs == NULL) goto oom_err; - upb_strtable_begin(&iter, &addtab); - for (n = 0; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { - add_defs[n++] = upb_value_getptr(upb_strtable_iter_value(&iter)); + /* We need an array of the defs in addtab, for passing to + * upb_refcounted_freeze(). */ + add_objs_size = upb_strtable_count(&addtab); + if (freeze_also) { + add_objs_size++; } - if (!upb_def_freeze(add_defs, n, status)) goto err; + add_defs = upb_gmalloc(sizeof(void*) * add_objs_size); + if (add_defs == NULL) goto oom_err; + upb_strtable_begin(&iter, &addtab); + for (add_n = 0; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { + add_defs[add_n++] = upb_value_getptr(upb_strtable_iter_value(&iter)); + } + + /* Validate defs. */ + if (!_upb_def_validate(add_defs, add_n, status)) { + goto err; + } + + /* Cheat a little and give the array a new type. + * This is probably undefined behavior, but this code will be deleted soon. */ + add_objs = (upb_refcounted**)add_defs; + + freeze_n = add_n; + if (freeze_also) { + add_objs[freeze_n++] = freeze_also; + } + + if (!upb_refcounted_freeze(add_objs, freeze_n, status, + UPB_MAX_MESSAGE_DEPTH * 2)) { + goto err; + } /* This must be delayed until all errors have been detected, since error * recovery code uses this table to cleanup defs. */ @@ -3999,8 +4130,8 @@ bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, /* TODO(haberman) we don't properly handle errors after this point (like * OOM in upb_strtable_insert() below). */ - for (i = 0; i < n; i++) { - upb_def *def = add_defs[i]; + for (i = 0; i < add_n; i++) { + upb_def *def = (upb_def*)add_objs[i]; const char *name = upb_def_fullname(def); upb_value v; bool success; @@ -4012,7 +4143,7 @@ bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, success = upb_strtable_insert(&s->symtab, name, upb_value_ptr(def)); UPB_ASSERT_VAR(success, success == true); } - free(add_defs); + upb_gfree(add_defs); return true; oom_err: @@ -4033,11 +4164,40 @@ err: { } } upb_strtable_uninit(&addtab); - free(add_defs); + upb_gfree(add_defs); assert(!upb_ok(status)); return false; } +bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, size_t n, + void *ref_donor, upb_status *status) { + return symtab_add(s, defs, n, ref_donor, NULL, status); +} + +bool upb_symtab_addfile(upb_symtab *s, upb_filedef *file, upb_status *status) { + size_t n; + size_t i; + upb_def **defs; + bool ret; + + n = upb_filedef_defcount(file); + defs = upb_gmalloc(sizeof(*defs) * n); + + if (defs == NULL) { + upb_status_seterrmsg(status, "Out of memory"); + return false; + } + + for (i = 0; i < n; i++) { + defs[i] = upb_filedef_mutabledef(file, i); + } + + ret = symtab_add(s, defs, n, NULL, upb_filedef_upcast_mutable(file), status); + + upb_gfree(defs); + return ret; +} + /* Iteration. */ static void advance_to_matching(upb_symtab_iter *iter) { @@ -4076,7 +4236,6 @@ const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter) { */ -#include #include #define UPB_MAXARRSIZE 16 /* 64k. */ @@ -4085,6 +4244,17 @@ const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter) { #define ARRAY_SIZE(x) \ ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) +#ifdef NDEBUG +static void upb_check_alloc(upb_table *t, upb_alloc *a) { + UPB_UNUSED(t); + UPB_UNUSED(a); +} +#else +static void upb_check_alloc(upb_table *t, upb_alloc *a) { + assert(t->alloc == a); +} +#endif + static const double MAX_LOAD = 0.85; /* The minimum utilization of the array part of a mixed hash/array table. This @@ -4102,11 +4272,11 @@ int log2ceil(uint64_t v) { return UPB_MIN(UPB_MAXARRSIZE, ret); } -char *upb_strdup(const char *s) { - return upb_strdup2(s, strlen(s)); +char *upb_strdup(const char *s, upb_alloc *a) { + return upb_strdup2(s, strlen(s), a); } -char *upb_strdup2(const char *s, size_t len) { +char *upb_strdup2(const char *s, size_t len, upb_alloc *a) { size_t n; char *p; @@ -4115,7 +4285,7 @@ char *upb_strdup2(const char *s, size_t len) { /* Always null-terminate, even if binary data; but don't rely on the input to * have a null-terminating byte since it may be a raw binary buffer. */ n = len + 1; - p = malloc(n); + p = upb_malloc(a, n); if (p) { memcpy(p, s, len); p[len] = 0; @@ -4163,16 +4333,20 @@ static bool isfull(upb_table *t) { } } -static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2) { +static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2, + upb_alloc *a) { size_t bytes; t->count = 0; t->ctype = ctype; t->size_lg2 = size_lg2; t->mask = upb_table_size(t) ? upb_table_size(t) - 1 : 0; +#ifndef NDEBUG + t->alloc = a; +#endif bytes = upb_table_size(t) * sizeof(upb_tabent); if (bytes > 0) { - t->entries = malloc(bytes); + t->entries = upb_malloc(a, bytes); if (!t->entries) return false; memset(mutable_entries(t), 0, bytes); } else { @@ -4181,7 +4355,10 @@ static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2) { return true; } -static void uninit(upb_table *t) { free(mutable_entries(t)); } +static void uninit(upb_table *t, upb_alloc *a) { + upb_check_alloc(t, a); + upb_free(a, mutable_entries(t)); +} static upb_tabent *emptyent(upb_table *t) { upb_tabent *e = mutable_entries(t) + upb_table_size(t); @@ -4334,8 +4511,8 @@ static size_t begin(const upb_table *t) { /* A simple "subclass" of upb_table that only adds a hash function for strings. */ -static upb_tabkey strcopy(lookupkey_t k2) { - char *str = malloc(k2.str.len + sizeof(uint32_t) + 1); +static upb_tabkey strcopy(lookupkey_t k2, upb_alloc *a) { + char *str = upb_malloc(a, k2.str.len + sizeof(uint32_t) + 1); if (str == NULL) return 0; memcpy(str, &k2.str.len, sizeof(uint32_t)); memcpy(str + sizeof(uint32_t), k2.str.str, k2.str.len + 1); @@ -4354,51 +4531,56 @@ static bool streql(upb_tabkey k1, lookupkey_t k2) { return len == k2.str.len && memcmp(str, k2.str.str, len) == 0; } -bool upb_strtable_init(upb_strtable *t, upb_ctype_t ctype) { - return init(&t->t, ctype, 2); +bool upb_strtable_init2(upb_strtable *t, upb_ctype_t ctype, upb_alloc *a) { + return init(&t->t, ctype, 2, a); } -void upb_strtable_uninit(upb_strtable *t) { +void upb_strtable_uninit2(upb_strtable *t, upb_alloc *a) { size_t i; for (i = 0; i < upb_table_size(&t->t); i++) - free((void*)t->t.entries[i].key); - uninit(&t->t); + upb_free(a, (void*)t->t.entries[i].key); + uninit(&t->t, a); } -bool upb_strtable_resize(upb_strtable *t, size_t size_lg2) { +bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a) { upb_strtable new_table; upb_strtable_iter i; - if (!init(&new_table.t, t->t.ctype, size_lg2)) + upb_check_alloc(&t->t, a); + + if (!init(&new_table.t, t->t.ctype, size_lg2, a)) return false; upb_strtable_begin(&i, t); for ( ; !upb_strtable_done(&i); upb_strtable_next(&i)) { - upb_strtable_insert2( + upb_strtable_insert3( &new_table, upb_strtable_iter_key(&i), upb_strtable_iter_keylength(&i), - upb_strtable_iter_value(&i)); + upb_strtable_iter_value(&i), + a); } - upb_strtable_uninit(t); + upb_strtable_uninit2(t, a); *t = new_table; return true; } -bool upb_strtable_insert2(upb_strtable *t, const char *k, size_t len, - upb_value v) { +bool upb_strtable_insert3(upb_strtable *t, const char *k, size_t len, + upb_value v, upb_alloc *a) { lookupkey_t key; upb_tabkey tabkey; uint32_t hash; + upb_check_alloc(&t->t, a); + if (isfull(&t->t)) { /* Need to resize. New table of double the size, add old elements to it. */ - if (!upb_strtable_resize(t, t->t.size_lg2 + 1)) { + if (!upb_strtable_resize(t, t->t.size_lg2 + 1, a)) { return false; } } key = strkey2(k, len); - tabkey = strcopy(key); + tabkey = strcopy(key, a); if (tabkey == 0) return false; hash = MurmurHash2(key.str.str, key.str.len, 0); @@ -4412,12 +4594,12 @@ bool upb_strtable_lookup2(const upb_strtable *t, const char *key, size_t len, return lookup(&t->t, strkey2(key, len), v, hash, &streql); } -bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len, - upb_value *val) { +bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len, + upb_value *val, upb_alloc *alloc) { uint32_t hash = MurmurHash2(key, strlen(key), 0); upb_tabkey tabkey; if (rm(&t->t, strkey2(key, len), val, &tabkey, hash, &streql)) { - free((void*)tabkey); + upb_free(alloc, (void*)tabkey); return true; } else { return false; @@ -4444,12 +4626,12 @@ bool upb_strtable_done(const upb_strtable_iter *i) { upb_tabent_isempty(str_tabent(i)); } -const char *upb_strtable_iter_key(upb_strtable_iter *i) { +const char *upb_strtable_iter_key(const upb_strtable_iter *i) { assert(!upb_strtable_done(i)); return upb_tabstr(str_tabent(i)->key, NULL); } -size_t upb_strtable_iter_keylength(upb_strtable_iter *i) { +size_t upb_strtable_iter_keylength(const upb_strtable_iter *i) { uint32_t len; assert(!upb_strtable_done(i)); upb_tabstr(str_tabent(i)->key, &len); @@ -4524,18 +4706,18 @@ static void check(upb_inttable *t) { } bool upb_inttable_sizedinit(upb_inttable *t, upb_ctype_t ctype, - size_t asize, int hsize_lg2) { + size_t asize, int hsize_lg2, upb_alloc *a) { size_t array_bytes; - if (!init(&t->t, ctype, hsize_lg2)) return false; + if (!init(&t->t, ctype, hsize_lg2, a)) return false; /* Always make the array part at least 1 long, so that we know key 0 * won't be in the hash part, which simplifies things. */ t->array_size = UPB_MAX(1, asize); t->array_count = 0; array_bytes = t->array_size * sizeof(upb_value); - t->array = malloc(array_bytes); + t->array = upb_malloc(a, array_bytes); if (!t->array) { - uninit(&t->t); + uninit(&t->t, a); return false; } memset(mutable_array(t), 0xff, array_bytes); @@ -4543,22 +4725,23 @@ bool upb_inttable_sizedinit(upb_inttable *t, upb_ctype_t ctype, return true; } -bool upb_inttable_init(upb_inttable *t, upb_ctype_t ctype) { - return upb_inttable_sizedinit(t, ctype, 0, 4); +bool upb_inttable_init2(upb_inttable *t, upb_ctype_t ctype, upb_alloc *a) { + return upb_inttable_sizedinit(t, ctype, 0, 4, a); } -void upb_inttable_uninit(upb_inttable *t) { - uninit(&t->t); - free(mutable_array(t)); +void upb_inttable_uninit2(upb_inttable *t, upb_alloc *a) { + uninit(&t->t, a); + upb_free(a, mutable_array(t)); } -bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) { - /* XXX: Table can't store value (uint64_t)-1. Need to somehow statically - * guarantee that this is not necessary, or fix the limitation. */ +bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val, + upb_alloc *a) { upb_tabval tabval; tabval.val = val.val; UPB_UNUSED(tabval); - assert(upb_arrhas(tabval)); + assert(upb_arrhas(tabval)); /* This will reject (uint64_t)-1. Fix this. */ + + upb_check_alloc(&t->t, a); if (key < t->array_size) { assert(!upb_arrhas(t->array[key])); @@ -4569,8 +4752,11 @@ bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) { /* Need to resize the hash part, but we re-use the array part. */ size_t i; upb_table new_table; - if (!init(&new_table, t->t.ctype, t->t.size_lg2 + 1)) + + if (!init(&new_table, t->t.ctype, t->t.size_lg2 + 1, a)) { return false; + } + for (i = begin(&t->t); i < upb_table_size(&t->t); i = next(&t->t, i)) { const upb_tabent *e = &t->t.entries[i]; uint32_t hash; @@ -4583,7 +4769,7 @@ bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) { assert(t->t.count == new_table.count); - uninit(&t->t); + uninit(&t->t, a); t->t = new_table; } insert(&t->t, intkey(key), key, val, upb_inthash(key), &inthash, &inteql); @@ -4629,8 +4815,9 @@ bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val) { return success; } -bool upb_inttable_push(upb_inttable *t, upb_value val) { - return upb_inttable_insert(t, upb_inttable_count(t), val); +bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a) { + upb_check_alloc(&t->t, a); + return upb_inttable_insert2(t, upb_inttable_count(t), val, a); } upb_value upb_inttable_pop(upb_inttable *t) { @@ -4640,8 +4827,10 @@ upb_value upb_inttable_pop(upb_inttable *t) { return val; } -bool upb_inttable_insertptr(upb_inttable *t, const void *key, upb_value val) { - return upb_inttable_insert(t, (uintptr_t)key, val); +bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val, + upb_alloc *a) { + upb_check_alloc(&t->t, a); + return upb_inttable_insert2(t, (uintptr_t)key, val, a); } bool upb_inttable_lookupptr(const upb_inttable *t, const void *key, @@ -4653,7 +4842,7 @@ bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val) { return upb_inttable_remove(t, (uintptr_t)key, val); } -void upb_inttable_compact(upb_inttable *t) { +void upb_inttable_compact2(upb_inttable *t, upb_alloc *a) { /* A power-of-two histogram of the table keys. */ size_t counts[UPB_MAXARRSIZE + 1] = {0}; @@ -4665,6 +4854,8 @@ void upb_inttable_compact(upb_inttable *t) { int size_lg2; upb_inttable new_t; + upb_check_alloc(&t->t, a); + upb_inttable_begin(&i, t); for (; !upb_inttable_done(&i); upb_inttable_next(&i)) { uintptr_t key = upb_inttable_iter_key(&i); @@ -4697,16 +4888,16 @@ void upb_inttable_compact(upb_inttable *t) { size_t hash_size = hash_count ? (hash_count / MAX_LOAD) + 1 : 0; size_t hashsize_lg2 = log2ceil(hash_size); - upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2); + upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2, a); upb_inttable_begin(&i, t); for (; !upb_inttable_done(&i); upb_inttable_next(&i)) { uintptr_t k = upb_inttable_iter_key(&i); - upb_inttable_insert(&new_t, k, upb_inttable_iter_value(&i)); + upb_inttable_insert2(&new_t, k, upb_inttable_iter_value(&i), a); } assert(new_t.array_size == arr_size); assert(new_t.t.size_lg2 == hashsize_lg2); } - upb_inttable_uninit(t); + upb_inttable_uninit2(t, a); *t = new_t; } @@ -4982,6 +5173,19 @@ static void nullz(upb_status *status) { memcpy(status->msg + sizeof(status->msg) - len, ellipsis, len); } + +/* upb_upberr *****************************************************************/ + +upb_errorspace upb_upberr = {"upb error"}; + +void upb_upberr_setoom(upb_status *status) { + status->error_space_ = &upb_upberr; + upb_status_seterrmsg(status, "Out of memory"); +} + + +/* upb_status *****************************************************************/ + void upb_status_clear(upb_status *status) { if (!status) return; status->ok_ = true; @@ -5020,58 +5224,298 @@ void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args) { nullz(status); } -void upb_status_seterrcode(upb_status *status, upb_errorspace *space, - int code) { - if (!status) return; - status->ok_ = false; - status->error_space_ = space; - status->code_ = code; - space->set_message(status, code); -} - void upb_status_copy(upb_status *to, const upb_status *from) { if (!to) return; *to = *from; } -/* This file was generated by upbc (the upb compiler). + + +/* upb_alloc ******************************************************************/ + +static void *upb_global_allocfunc(upb_alloc *alloc, void *ptr, size_t oldsize, + size_t size) { + UPB_UNUSED(alloc); + UPB_UNUSED(oldsize); + if (size == 0) { + free(ptr); + return NULL; + } else { + return realloc(ptr, size); + } +} + +upb_alloc upb_alloc_global = {&upb_global_allocfunc}; + + +/* upb_arena ******************************************************************/ + +/* Be conservative and choose 16 in case anyone is using SSE. */ +static const size_t maxalign = 16; + +static size_t align_up(size_t size) { + return ((size + maxalign - 1) / maxalign) * maxalign; +} + +typedef struct mem_block { + struct mem_block *next; + size_t size; + size_t used; + bool owned; + /* Data follows. */ +} mem_block; + +typedef struct cleanup_ent { + struct cleanup_ent *next; + upb_cleanup_func *cleanup; + void *ud; +} cleanup_ent; + +static void upb_arena_addblock(upb_arena *a, void *ptr, size_t size, + bool owned) { + mem_block *block = ptr; + + block->next = a->block_head; + block->size = size; + block->used = align_up(sizeof(mem_block)); + block->owned = owned; + + a->block_head = block; + + /* TODO(haberman): ASAN poison. */ +} + + +static mem_block *upb_arena_allocblock(upb_arena *a, size_t size) { + size_t block_size = UPB_MAX(size, a->next_block_size) + sizeof(mem_block); + mem_block *block = upb_malloc(a->block_alloc, block_size); + + if (!block) { + return NULL; + } + + upb_arena_addblock(a, block, block_size, true); + a->next_block_size = UPB_MIN(block_size * 2, a->max_block_size); + + return block; +} + +static void *upb_arena_doalloc(upb_alloc *alloc, void *ptr, size_t oldsize, + size_t size) { + upb_arena *a = (upb_arena*)alloc; /* upb_alloc is initial member. */ + mem_block *block = a->block_head; + void *ret; + + if (size == 0) { + return NULL; /* We are an arena, don't need individual frees. */ + } + + size = align_up(size); + + /* TODO(haberman): special-case if this is a realloc of the last alloc? */ + + if (!block || block->size - block->used < size) { + /* Slow path: have to allocate a new block. */ + block = upb_arena_allocblock(a, size); + + if (!block) { + return NULL; /* Out of memory. */ + } + } + + ret = (char*)block + block->used; + block->used += size; + + if (oldsize > 0) { + memcpy(ret, ptr, oldsize); /* Preserve existing data. */ + } + + /* TODO(haberman): ASAN unpoison. */ + + a->bytes_allocated += size; + return ret; +} + +/* Public Arena API ***********************************************************/ + +void upb_arena_init(upb_arena *a) { + a->alloc.func = &upb_arena_doalloc; + a->block_alloc = &upb_alloc_global; + a->bytes_allocated = 0; + a->next_block_size = 256; + a->max_block_size = 16384; + a->cleanup_head = NULL; + a->block_head = NULL; +} + +void upb_arena_init2(upb_arena *a, void *mem, size_t size, upb_alloc *alloc) { + upb_arena_init(a); + + if (size > sizeof(mem_block)) { + upb_arena_addblock(a, mem, size, false); + } + + if (alloc) { + a->block_alloc = alloc; + } +} + +void upb_arena_uninit(upb_arena *a) { + cleanup_ent *ent = a->cleanup_head; + mem_block *block = a->block_head; + + while (ent) { + ent->cleanup(ent->ud); + ent = ent->next; + } + + /* Must do this after running cleanup functions, because this will delete + * the memory we store our cleanup entries in! */ + while (block) { + mem_block *next = block->next; + + if (block->owned) { + upb_free(a->block_alloc, block); + } + + block = next; + } +} + +bool upb_arena_addcleanup(upb_arena *a, upb_cleanup_func *func, void *ud) { + cleanup_ent *ent = upb_malloc(&a->alloc, sizeof(cleanup_ent)); + if (!ent) { + return false; /* Out of memory. */ + } + + ent->cleanup = func; + ent->ud = ud; + ent->next = a->cleanup_head; + a->cleanup_head = ent; + + return true; +} + +size_t upb_arena_bytesallocated(const upb_arena *a) { + return a->bytes_allocated; +} + + +/* Standard error functions ***************************************************/ + +static bool default_err(void *ud, const upb_status *status) { + UPB_UNUSED(ud); + UPB_UNUSED(status); + return false; +} + +static bool write_err_to(void *ud, const upb_status *status) { + upb_status *copy_to = ud; + upb_status_copy(copy_to, status); + return false; +} + + +/* upb_env ********************************************************************/ + +void upb_env_initonly(upb_env *e) { + e->ok_ = true; + e->error_func_ = &default_err; + e->error_ud_ = NULL; +} + +void upb_env_init(upb_env *e) { + upb_arena_init(&e->arena_); + upb_env_initonly(e); +} + +void upb_env_init2(upb_env *e, void *mem, size_t n, upb_alloc *alloc) { + upb_arena_init2(&e->arena_, mem, n, alloc); + upb_env_initonly(e); +} + +void upb_env_uninit(upb_env *e) { + upb_arena_uninit(&e->arena_); +} + +void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud) { + e->error_func_ = func; + e->error_ud_ = ud; +} + +void upb_env_reporterrorsto(upb_env *e, upb_status *s) { + e->error_func_ = &write_err_to; + e->error_ud_ = s; +} + +bool upb_env_reporterror(upb_env *e, const upb_status *status) { + e->ok_ = false; + return e->error_func_(e->error_ud_, status); +} + +void *upb_env_malloc(upb_env *e, size_t size) { + return upb_malloc(&e->arena_.alloc, size); +} + +void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) { + return upb_realloc(&e->arena_.alloc, ptr, oldsize, size); +} + +void upb_env_free(upb_env *e, void *ptr) { + upb_free(&e->arena_.alloc, ptr); +} + +bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) { + return upb_arena_addcleanup(&e->arena_, func, ud); +} + +size_t upb_env_bytesallocated(const upb_env *e) { + return upb_arena_bytesallocated(&e->arena_); +} +/* This file was generated by upbc (the upb compiler) from the input + * file: + * + * upb/descriptor/descriptor.proto + * * Do not edit -- your changes will be discarded when the file is * regenerated. */ +#include + static const upb_msgdef msgs[22]; static const upb_fielddef fields[105]; static const upb_enumdef enums[5]; -static const upb_tabent strentries[268]; +static const upb_tabent strentries[236]; static const upb_tabent intentries[18]; static const upb_tabval arrays[184]; #ifdef UPB_DEBUG_REFS -static upb_inttable reftables[266]; +static upb_inttable reftables[264]; #endif static const upb_msgdef msgs[22] = { - UPB_MSGDEF_INIT("google.protobuf.DescriptorProto", 40, 8, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[0], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[0]),&reftables[0], &reftables[1]), - UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ExtensionRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[11], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[16]),&reftables[2], &reftables[3]), - UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ReservedRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[14], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[20]),&reftables[4], &reftables[5]), - UPB_MSGDEF_INIT("google.protobuf.EnumDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[17], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[24]),&reftables[6], &reftables[7]), - UPB_MSGDEF_INIT("google.protobuf.EnumOptions", 8, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[0], &arrays[21], 4, 2), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[28]),&reftables[8], &reftables[9]), - UPB_MSGDEF_INIT("google.protobuf.EnumValueDescriptorProto", 8, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[25], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[32]),&reftables[10], &reftables[11]), - UPB_MSGDEF_INIT("google.protobuf.EnumValueOptions", 7, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[2], &arrays[29], 2, 1), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[36]),&reftables[12], &reftables[13]), - UPB_MSGDEF_INIT("google.protobuf.FieldDescriptorProto", 23, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[31], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[40]),&reftables[14], &reftables[15]), - UPB_MSGDEF_INIT("google.protobuf.FieldOptions", 12, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[4], &arrays[42], 11, 6), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[56]),&reftables[16], &reftables[17]), - UPB_MSGDEF_INIT("google.protobuf.FileDescriptorProto", 42, 6, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[53], 13, 12), UPB_STRTABLE_INIT(12, 15, UPB_CTYPE_PTR, 4, &strentries[72]),&reftables[18], &reftables[19]), - UPB_MSGDEF_INIT("google.protobuf.FileDescriptorSet", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[66], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[88]),&reftables[20], &reftables[21]), - UPB_MSGDEF_INIT("google.protobuf.FileOptions", 31, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[6], &arrays[68], 39, 15), UPB_STRTABLE_INIT(16, 31, UPB_CTYPE_PTR, 5, &strentries[92]),&reftables[22], &reftables[23]), - UPB_MSGDEF_INIT("google.protobuf.MessageOptions", 10, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[8], &arrays[107], 8, 4), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[124]),&reftables[24], &reftables[25]), - UPB_MSGDEF_INIT("google.protobuf.MethodDescriptorProto", 15, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[115], 7, 6), UPB_STRTABLE_INIT(6, 7, UPB_CTYPE_PTR, 3, &strentries[132]),&reftables[26], &reftables[27]), - UPB_MSGDEF_INIT("google.protobuf.MethodOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[10], &arrays[122], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[140]),&reftables[28], &reftables[29]), - UPB_MSGDEF_INIT("google.protobuf.OneofDescriptorProto", 5, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[123], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[144]),&reftables[30], &reftables[31]), - UPB_MSGDEF_INIT("google.protobuf.ServiceDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[125], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[148]),&reftables[32], &reftables[33]), - UPB_MSGDEF_INIT("google.protobuf.ServiceOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[14], &arrays[129], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[152]),&reftables[34], &reftables[35]), - UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[130], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[156]),&reftables[36], &reftables[37]), - UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo.Location", 19, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[132], 7, 5), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[160]),&reftables[38], &reftables[39]), - UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption", 18, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[139], 9, 7), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[168]),&reftables[40], &reftables[41]), - UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption.NamePart", 6, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[148], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[184]),&reftables[42], &reftables[43]), + UPB_MSGDEF_INIT("google.protobuf.DescriptorProto", 40, 8, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[0], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[0]), false, UPB_SYNTAX_PROTO2, &reftables[0], &reftables[1]), + UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ExtensionRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[11], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[16]), false, UPB_SYNTAX_PROTO2, &reftables[2], &reftables[3]), + UPB_MSGDEF_INIT("google.protobuf.DescriptorProto.ReservedRange", 4, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[14], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[20]), false, UPB_SYNTAX_PROTO2, &reftables[4], &reftables[5]), + UPB_MSGDEF_INIT("google.protobuf.EnumDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[17], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[24]), false, UPB_SYNTAX_PROTO2, &reftables[6], &reftables[7]), + UPB_MSGDEF_INIT("google.protobuf.EnumOptions", 8, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[0], &arrays[21], 4, 2), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[28]), false, UPB_SYNTAX_PROTO2, &reftables[8], &reftables[9]), + UPB_MSGDEF_INIT("google.protobuf.EnumValueDescriptorProto", 8, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[25], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[32]), false, UPB_SYNTAX_PROTO2, &reftables[10], &reftables[11]), + UPB_MSGDEF_INIT("google.protobuf.EnumValueOptions", 7, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[2], &arrays[29], 2, 1), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[36]), false, UPB_SYNTAX_PROTO2, &reftables[12], &reftables[13]), + UPB_MSGDEF_INIT("google.protobuf.FieldDescriptorProto", 23, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[31], 11, 10), UPB_STRTABLE_INIT(10, 15, UPB_CTYPE_PTR, 4, &strentries[40]), false, UPB_SYNTAX_PROTO2, &reftables[14], &reftables[15]), + UPB_MSGDEF_INIT("google.protobuf.FieldOptions", 12, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[4], &arrays[42], 11, 6), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[56]), false, UPB_SYNTAX_PROTO2, &reftables[16], &reftables[17]), + UPB_MSGDEF_INIT("google.protobuf.FileDescriptorProto", 42, 6, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[53], 13, 12), UPB_STRTABLE_INIT(12, 15, UPB_CTYPE_PTR, 4, &strentries[72]), false, UPB_SYNTAX_PROTO2, &reftables[18], &reftables[19]), + UPB_MSGDEF_INIT("google.protobuf.FileDescriptorSet", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[66], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[88]), false, UPB_SYNTAX_PROTO2, &reftables[20], &reftables[21]), + UPB_MSGDEF_INIT("google.protobuf.FileOptions", 31, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[6], &arrays[68], 39, 15), UPB_STRTABLE_INIT(16, 31, UPB_CTYPE_PTR, 5, &strentries[92]), false, UPB_SYNTAX_PROTO2, &reftables[22], &reftables[23]), + UPB_MSGDEF_INIT("google.protobuf.MessageOptions", 10, 1, UPB_INTTABLE_INIT(1, 1, UPB_CTYPE_PTR, 1, &intentries[8], &arrays[107], 8, 4), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[124]), false, UPB_SYNTAX_PROTO2, &reftables[24], &reftables[25]), + UPB_MSGDEF_INIT("google.protobuf.MethodDescriptorProto", 15, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[115], 7, 6), UPB_STRTABLE_INIT(6, 7, UPB_CTYPE_PTR, 3, &strentries[132]), false, UPB_SYNTAX_PROTO2, &reftables[26], &reftables[27]), + UPB_MSGDEF_INIT("google.protobuf.MethodOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[10], &arrays[122], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[140]), false, UPB_SYNTAX_PROTO2, &reftables[28], &reftables[29]), + UPB_MSGDEF_INIT("google.protobuf.OneofDescriptorProto", 5, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[123], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[144]), false, UPB_SYNTAX_PROTO2, &reftables[30], &reftables[31]), + UPB_MSGDEF_INIT("google.protobuf.ServiceDescriptorProto", 11, 2, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[125], 4, 3), UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_PTR, 2, &strentries[148]), false, UPB_SYNTAX_PROTO2, &reftables[32], &reftables[33]), + UPB_MSGDEF_INIT("google.protobuf.ServiceOptions", 7, 1, UPB_INTTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &intentries[14], &arrays[129], 1, 0), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[152]), false, UPB_SYNTAX_PROTO2, &reftables[34], &reftables[35]), + UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo", 6, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[130], 2, 1), UPB_STRTABLE_INIT(1, 3, UPB_CTYPE_PTR, 2, &strentries[156]), false, UPB_SYNTAX_PROTO2, &reftables[36], &reftables[37]), + UPB_MSGDEF_INIT("google.protobuf.SourceCodeInfo.Location", 19, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[132], 7, 5), UPB_STRTABLE_INIT(5, 7, UPB_CTYPE_PTR, 3, &strentries[160]), false, UPB_SYNTAX_PROTO2, &reftables[38], &reftables[39]), + UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption", 18, 1, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[139], 9, 7), UPB_STRTABLE_INIT(7, 15, UPB_CTYPE_PTR, 4, &strentries[168]), false, UPB_SYNTAX_PROTO2, &reftables[40], &reftables[41]), + UPB_MSGDEF_INIT("google.protobuf.UninterpretedOption.NamePart", 6, 0, UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_PTR, 0, NULL, &arrays[148], 3, 2), UPB_STRTABLE_INIT(2, 3, UPB_CTYPE_PTR, 2, &strentries[184]), false, UPB_SYNTAX_PROTO2, &reftables[42], &reftables[43]), }; static const upb_fielddef fields[105] = { @@ -5084,18 +5528,18 @@ static const upb_fielddef fields[105] = { UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "ctype", 1, &msgs[8], (const upb_def*)(&enums[2]), 6, 1, {0},&reftables[56], &reftables[57]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "default_value", 7, &msgs[7], NULL, 16, 7, {0},&reftables[58], &reftables[59]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_STRING, 0, false, false, false, false, "dependency", 3, &msgs[9], NULL, 30, 8, {0},&reftables[60], &reftables[61]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 1, &msgs[6], NULL, 6, 1, {0},&reftables[62], &reftables[63]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 3, &msgs[4], NULL, 7, 2, {0},&reftables[64], &reftables[65]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 33, &msgs[17], NULL, 6, 1, {0},&reftables[66], &reftables[67]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 3, &msgs[8], NULL, 8, 3, {0},&reftables[68], &reftables[69]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 33, &msgs[14], NULL, 6, 1, {0},&reftables[70], &reftables[71]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 3, &msgs[12], NULL, 8, 3, {0},&reftables[72], &reftables[73]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 23, &msgs[11], NULL, 21, 10, {0},&reftables[74], &reftables[75]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 3, &msgs[12], NULL, 8, 3, {0},&reftables[62], &reftables[63]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 3, &msgs[8], NULL, 8, 3, {0},&reftables[64], &reftables[65]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 33, &msgs[14], NULL, 6, 1, {0},&reftables[66], &reftables[67]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 23, &msgs[11], NULL, 21, 10, {0},&reftables[68], &reftables[69]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 3, &msgs[4], NULL, 7, 2, {0},&reftables[70], &reftables[71]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 33, &msgs[17], NULL, 6, 1, {0},&reftables[72], &reftables[73]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 1, &msgs[6], NULL, 6, 1, {0},&reftables[74], &reftables[75]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_DOUBLE, 0, false, false, false, false, "double_value", 6, &msgs[20], NULL, 11, 4, {0},&reftables[76], &reftables[77]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "end", 2, &msgs[2], NULL, 3, 1, {0},&reftables[78], &reftables[79]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "end", 2, &msgs[1], NULL, 3, 1, {0},&reftables[80], &reftables[81]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 5, &msgs[9], (const upb_def*)(&msgs[3]), 13, 1, {0},&reftables[82], &reftables[83]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 4, &msgs[0], (const upb_def*)(&msgs[3]), 18, 2, {0},&reftables[84], &reftables[85]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 4, &msgs[0], (const upb_def*)(&msgs[3]), 18, 2, {0},&reftables[82], &reftables[83]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 5, &msgs[9], (const upb_def*)(&msgs[3]), 13, 1, {0},&reftables[84], &reftables[85]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "extendee", 2, &msgs[7], NULL, 7, 2, {0},&reftables[86], &reftables[87]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension", 6, &msgs[0], (const upb_def*)(&msgs[7]), 24, 4, {0},&reftables[88], &reftables[89]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension", 7, &msgs[9], (const upb_def*)(&msgs[7]), 19, 3, {0},&reftables[90], &reftables[91]), @@ -5124,32 +5568,32 @@ static const upb_fielddef fields[105] = { UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "message_set_wire_format", 1, &msgs[12], NULL, 6, 1, {0},&reftables[136], &reftables[137]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "message_type", 4, &msgs[9], (const upb_def*)(&msgs[0]), 10, 0, {0},&reftables[138], &reftables[139]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "method", 2, &msgs[16], (const upb_def*)(&msgs[13]), 6, 0, {0},&reftables[140], &reftables[141]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[9], NULL, 22, 6, {0},&reftables[142], &reftables[143]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[5], NULL, 4, 1, {0},&reftables[144], &reftables[145]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[13], NULL, 4, 1, {0},&reftables[146], &reftables[147]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[3], NULL, 8, 2, {0},&reftables[142], &reftables[143]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[15], NULL, 2, 0, {0},&reftables[144], &reftables[145]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "name", 2, &msgs[20], (const upb_def*)(&msgs[21]), 5, 0, {0},&reftables[146], &reftables[147]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[0], NULL, 32, 8, {0},&reftables[148], &reftables[149]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[3], NULL, 8, 2, {0},&reftables[150], &reftables[151]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[15], NULL, 2, 0, {0},&reftables[152], &reftables[153]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[16], NULL, 8, 2, {0},&reftables[154], &reftables[155]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[7], NULL, 4, 1, {0},&reftables[156], &reftables[157]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "name", 2, &msgs[20], (const upb_def*)(&msgs[21]), 5, 0, {0},&reftables[158], &reftables[159]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[5], NULL, 4, 1, {0},&reftables[150], &reftables[151]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[9], NULL, 22, 6, {0},&reftables[152], &reftables[153]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[7], NULL, 4, 1, {0},&reftables[154], &reftables[155]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[13], NULL, 4, 1, {0},&reftables[156], &reftables[157]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[16], NULL, 8, 2, {0},&reftables[158], &reftables[159]), UPB_FIELDDEF_INIT(UPB_LABEL_REQUIRED, UPB_TYPE_STRING, 0, false, false, false, false, "name_part", 1, &msgs[21], NULL, 2, 0, {0},&reftables[160], &reftables[161]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT64, UPB_INTFMT_VARIABLE, false, false, false, false, "negative_int_value", 5, &msgs[20], NULL, 10, 3, {0},&reftables[162], &reftables[163]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "nested_type", 3, &msgs[0], (const upb_def*)(&msgs[0]), 15, 1, {0},&reftables[164], &reftables[165]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "no_standard_descriptor_accessor", 2, &msgs[12], NULL, 7, 2, {0},&reftables[166], &reftables[167]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "number", 3, &msgs[7], NULL, 10, 3, {0},&reftables[168], &reftables[169]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "number", 2, &msgs[5], NULL, 7, 2, {0},&reftables[170], &reftables[171]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "number", 2, &msgs[5], NULL, 7, 2, {0},&reftables[168], &reftables[169]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "number", 3, &msgs[7], NULL, 10, 3, {0},&reftables[170], &reftables[171]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "objc_class_prefix", 36, &msgs[11], NULL, 24, 13, {0},&reftables[172], &reftables[173]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "oneof_decl", 8, &msgs[0], (const upb_def*)(&msgs[15]), 28, 6, {0},&reftables[174], &reftables[175]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "oneof_index", 9, &msgs[7], NULL, 19, 8, {0},&reftables[176], &reftables[177]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "optimize_for", 9, &msgs[11], (const upb_def*)(&enums[4]), 12, 3, {0},&reftables[178], &reftables[179]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 4, &msgs[13], (const upb_def*)(&msgs[14]), 3, 0, {0},&reftables[180], &reftables[181]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[3], (const upb_def*)(&msgs[4]), 7, 1, {0},&reftables[182], &reftables[183]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 7, &msgs[0], (const upb_def*)(&msgs[12]), 25, 5, {0},&reftables[184], &reftables[185]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[5], (const upb_def*)(&msgs[6]), 3, 0, {0},&reftables[186], &reftables[187]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[9], (const upb_def*)(&msgs[11]), 20, 4, {0},&reftables[188], &reftables[189]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[16], (const upb_def*)(&msgs[17]), 7, 1, {0},&reftables[190], &reftables[191]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[7], (const upb_def*)(&msgs[8]), 3, 0, {0},&reftables[192], &reftables[193]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 7, &msgs[0], (const upb_def*)(&msgs[12]), 25, 5, {0},&reftables[180], &reftables[181]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[9], (const upb_def*)(&msgs[11]), 20, 4, {0},&reftables[182], &reftables[183]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 4, &msgs[13], (const upb_def*)(&msgs[14]), 3, 0, {0},&reftables[184], &reftables[185]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[7], (const upb_def*)(&msgs[8]), 3, 0, {0},&reftables[186], &reftables[187]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[16], (const upb_def*)(&msgs[17]), 7, 1, {0},&reftables[188], &reftables[189]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[5], (const upb_def*)(&msgs[6]), 3, 0, {0},&reftables[190], &reftables[191]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[3], (const upb_def*)(&msgs[4]), 7, 1, {0},&reftables[192], &reftables[193]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "output_type", 3, &msgs[13], NULL, 10, 3, {0},&reftables[194], &reftables[195]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "package", 2, &msgs[9], NULL, 25, 7, {0},&reftables[196], &reftables[197]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "packed", 2, &msgs[8], NULL, 7, 2, {0},&reftables[198], &reftables[199]), @@ -5163,20 +5607,20 @@ static const upb_fielddef fields[105] = { UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "service", 6, &msgs[9], (const upb_def*)(&msgs[16]), 16, 2, {0},&reftables[214], &reftables[215]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "source_code_info", 9, &msgs[9], (const upb_def*)(&msgs[18]), 21, 5, {0},&reftables[216], &reftables[217]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, true, "span", 2, &msgs[19], NULL, 7, 1, {0},&reftables[218], &reftables[219]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "start", 1, &msgs[1], NULL, 2, 0, {0},&reftables[220], &reftables[221]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "start", 1, &msgs[2], NULL, 2, 0, {0},&reftables[222], &reftables[223]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "start", 1, &msgs[2], NULL, 2, 0, {0},&reftables[220], &reftables[221]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "start", 1, &msgs[1], NULL, 2, 0, {0},&reftables[222], &reftables[223]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BYTES, 0, false, false, false, false, "string_value", 7, &msgs[20], NULL, 12, 5, {0},&reftables[224], &reftables[225]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "syntax", 12, &msgs[9], NULL, 39, 11, {0},&reftables[226], &reftables[227]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "trailing_comments", 4, &msgs[19], NULL, 11, 3, {0},&reftables[228], &reftables[229]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "type", 5, &msgs[7], (const upb_def*)(&enums[1]), 12, 5, {0},&reftables[230], &reftables[231]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "type_name", 6, &msgs[7], NULL, 13, 6, {0},&reftables[232], &reftables[233]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[17], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[234], &reftables[235]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[11], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[234], &reftables[235]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[12], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[236], &reftables[237]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[8], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[238], &reftables[239]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[14], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[240], &reftables[241]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[11], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[242], &reftables[243]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[4], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[244], &reftables[245]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[6], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[246], &reftables[247]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[6], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[238], &reftables[239]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[4], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[240], &reftables[241]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[8], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[242], &reftables[243]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[14], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[244], &reftables[245]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[17], (const upb_def*)(&msgs[20]), 5, 0, {0},&reftables[246], &reftables[247]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "value", 2, &msgs[3], (const upb_def*)(&msgs[5]), 6, 0, {0},&reftables[248], &reftables[249]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "weak", 10, &msgs[8], NULL, 11, 6, {0},&reftables[250], &reftables[251]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "weak_dependency", 11, &msgs[9], NULL, 38, 10, {0},&reftables[252], &reftables[253]), @@ -5190,7 +5634,7 @@ static const upb_enumdef enums[5] = { UPB_ENUMDEF_INIT("google.protobuf.FileOptions.OptimizeMode", UPB_STRTABLE_INIT(3, 3, UPB_CTYPE_INT32, 2, &strentries[232]), UPB_INTTABLE_INIT(0, 0, UPB_CTYPE_CSTR, 0, NULL, &arrays[180], 4, 3), 0, &reftables[262], &reftables[263]), }; -static const upb_tabent strentries[268] = { +static const upb_tabent strentries[236] = { {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "extension"), UPB_TABVALUE_PTR_INIT(&fields[22]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\015", "\000", "\000", "\000", "reserved_name"), UPB_TABVALUE_PTR_INIT(&fields[82]), NULL}, @@ -5204,50 +5648,50 @@ static const upb_tabent strentries[268] = { {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "nested_type"), UPB_TABVALUE_PTR_INIT(&fields[60]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\016", "\000", "\000", "\000", "reserved_range"), UPB_TABVALUE_PTR_INIT(&fields[83]), NULL}, - {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[70]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[68]), NULL}, {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "oneof_decl"), UPB_TABVALUE_PTR_INIT(&fields[65]), NULL}, - {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "enum_type"), UPB_TABVALUE_PTR_INIT(&fields[20]), &strentries[13]}, - {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "start"), UPB_TABVALUE_PTR_INIT(&fields[88]), NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "enum_type"), UPB_TABVALUE_PTR_INIT(&fields[19]), &strentries[13]}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "start"), UPB_TABVALUE_PTR_INIT(&fields[89]), NULL}, {UPB_TABKEY_STR("\003", "\000", "\000", "\000", "end"), UPB_TABVALUE_PTR_INIT(&fields[18]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "start"), UPB_TABVALUE_PTR_INIT(&fields[89]), NULL}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "start"), UPB_TABVALUE_PTR_INIT(&fields[88]), NULL}, {UPB_TABKEY_STR("\003", "\000", "\000", "\000", "end"), UPB_TABVALUE_PTR_INIT(&fields[17]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "value"), UPB_TABVALUE_PTR_INIT(&fields[102]), NULL}, - {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[69]), NULL}, - {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[53]), &strentries[26]}, - {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[100]), NULL}, - {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[10]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[74]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[49]), &strentries[26]}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[98]), NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[13]), NULL}, {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "allow_alias"), UPB_TABVALUE_PTR_INIT(&fields[1]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "number"), UPB_TABVALUE_PTR_INIT(&fields[63]), NULL}, + {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "number"), UPB_TABVALUE_PTR_INIT(&fields[62]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[71]), NULL}, - {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[50]), &strentries[34]}, - {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[101]), NULL}, - {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[9]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[73]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[53]), &strentries[34]}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[97]), NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[15]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "oneof_index"), UPB_TABVALUE_PTR_INIT(&fields[66]), NULL}, {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "label"), UPB_TABVALUE_PTR_INIT(&fields[40]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[56]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[55]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "number"), UPB_TABVALUE_PTR_INIT(&fields[62]), &strentries[53]}, + {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "number"), UPB_TABVALUE_PTR_INIT(&fields[63]), &strentries[53]}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\010", "\000", "\000", "\000", "extendee"), UPB_TABVALUE_PTR_INIT(&fields[21]), NULL}, {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "type_name"), UPB_TABVALUE_PTR_INIT(&fields[94]), NULL}, {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "json_name"), UPB_TABVALUE_PTR_INIT(&fields[38]), NULL}, {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "type"), UPB_TABVALUE_PTR_INIT(&fields[93]), &strentries[50]}, {UPB_TABKEY_STR("\015", "\000", "\000", "\000", "default_value"), UPB_TABVALUE_PTR_INIT(&fields[7]), NULL}, - {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[74]), NULL}, - {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[97]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[71]), NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[99]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "weak"), UPB_TABVALUE_PTR_INIT(&fields[103]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, @@ -5260,13 +5704,13 @@ static const upb_tabent strentries[268] = { {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "ctype"), UPB_TABVALUE_PTR_INIT(&fields[6]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "jstype"), UPB_TABVALUE_PTR_INIT(&fields[39]), NULL}, - {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[12]), NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[10]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "extension"), UPB_TABVALUE_PTR_INIT(&fields[23]), NULL}, {UPB_TABKEY_STR("\017", "\000", "\000", "\000", "weak_dependency"), UPB_TABVALUE_PTR_INIT(&fields[104]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[49]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[54]), NULL}, {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "service"), UPB_TABVALUE_PTR_INIT(&fields[85]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "source_code_info"), UPB_TABVALUE_PTR_INIT(&fields[86]), NULL}, @@ -5276,8 +5720,8 @@ static const upb_tabent strentries[268] = { {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "dependency"), UPB_TABVALUE_PTR_INIT(&fields[8]), NULL}, {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "message_type"), UPB_TABVALUE_PTR_INIT(&fields[47]), NULL}, {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "package"), UPB_TABVALUE_PTR_INIT(&fields[76]), NULL}, - {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[72]), &strentries[86]}, - {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "enum_type"), UPB_TABVALUE_PTR_INIT(&fields[19]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[69]), &strentries[86]}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "enum_type"), UPB_TABVALUE_PTR_INIT(&fields[20]), NULL}, {UPB_TABKEY_STR("\021", "\000", "\000", "\000", "public_dependency"), UPB_TABVALUE_PTR_INIT(&fields[80]), &strentries[85]}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "file"), UPB_TABVALUE_PTR_INIT(&fields[26]), NULL}, @@ -5299,7 +5743,7 @@ static const upb_tabent strentries[268] = { {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "java_outer_classname"), UPB_TABVALUE_PTR_INIT(&fields[34]), NULL}, - {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[99]), NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[95]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, @@ -5312,7 +5756,7 @@ static const upb_tabent strentries[268] = { {UPB_TABKEY_STR("\023", "\000", "\000", "\000", "py_generic_services"), UPB_TABVALUE_PTR_INIT(&fields[81]), NULL}, {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "optimize_for"), UPB_TABVALUE_PTR_INIT(&fields[67]), NULL}, {UPB_TABKEY_STR("\026", "\000", "\000", "\000", "java_string_check_utf8"), UPB_TABVALUE_PTR_INIT(&fields[36]), NULL}, - {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[15]), &strentries[119]}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[12]), &strentries[119]}, {UPB_TABKEY_STR("\021", "\000", "\000", "\000", "objc_class_prefix"), UPB_TABVALUE_PTR_INIT(&fields[64]), NULL}, {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "cc_enable_arenas"), UPB_TABVALUE_PTR_INIT(&fields[2]), NULL}, {UPB_TABKEY_STR("\027", "\000", "\000", "\000", "message_set_wire_format"), UPB_TABVALUE_PTR_INIT(&fields[46]), &strentries[128]}, @@ -5320,35 +5764,35 @@ static const upb_tabent strentries[268] = { {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[96]), NULL}, - {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[14]), NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[9]), NULL}, {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "map_entry"), UPB_TABVALUE_PTR_INIT(&fields[45]), NULL}, {UPB_TABKEY_STR("\037", "\000", "\000", "\000", "no_standard_descriptor_accessor"), UPB_TABVALUE_PTR_INIT(&fields[61]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "client_streaming"), UPB_TABVALUE_PTR_INIT(&fields[4]), NULL}, {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "server_streaming"), UPB_TABVALUE_PTR_INIT(&fields[84]), NULL}, - {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[51]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[56]), NULL}, {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "input_type"), UPB_TABVALUE_PTR_INIT(&fields[29]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "output_type"), UPB_TABVALUE_PTR_INIT(&fields[75]), NULL}, - {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[68]), NULL}, - {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[98]), NULL}, - {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[13]), NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[54]), NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[73]), &strentries[150]}, - {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "method"), UPB_TABVALUE_PTR_INIT(&fields[48]), NULL}, - {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[55]), &strentries[149]}, - {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[95]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[70]), NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[100]), NULL}, {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[11]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[50]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[72]), &strentries[150]}, + {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "method"), UPB_TABVALUE_PTR_INIT(&fields[48]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[57]), &strentries[149]}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[101]), NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[14]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\010", "\000", "\000", "\000", "location"), UPB_TABVALUE_PTR_INIT(&fields[44]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, @@ -5362,7 +5806,7 @@ static const upb_tabent strentries[268] = { {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "double_value"), UPB_TABVALUE_PTR_INIT(&fields[16]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[57]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[51]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, @@ -5427,59 +5871,27 @@ static const upb_tabent strentries[268] = { {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "SPEED"), UPB_TABVALUE_INT_INIT(1), &strentries[235]}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "LITE_RUNTIME"), UPB_TABVALUE_INT_INIT(3), NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_STR("\047", "\000", "\000", "\000", "google.protobuf.SourceCodeInfo.Location"), UPB_TABVALUE_PTR_INIT(&msgs[19]), NULL}, - {UPB_TABKEY_STR("\043", "\000", "\000", "\000", "google.protobuf.UninterpretedOption"), UPB_TABVALUE_PTR_INIT(&msgs[20]), NULL}, - {UPB_TABKEY_STR("\043", "\000", "\000", "\000", "google.protobuf.FileDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[9]), NULL}, - {UPB_TABKEY_STR("\045", "\000", "\000", "\000", "google.protobuf.MethodDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[13]), NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_STR("\040", "\000", "\000", "\000", "google.protobuf.EnumValueOptions"), UPB_TABVALUE_PTR_INIT(&msgs[6]), NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_STR("\055", "\000", "\000", "\000", "google.protobuf.DescriptorProto.ReservedRange"), UPB_TABVALUE_PTR_INIT(&msgs[2]), NULL}, - {UPB_TABKEY_STR("\037", "\000", "\000", "\000", "google.protobuf.DescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[0]), &strentries[248]}, - {UPB_TABKEY_STR("\041", "\000", "\000", "\000", "google.protobuf.FileDescriptorSet"), UPB_TABVALUE_PTR_INIT(&msgs[10]), &strentries[267]}, - {UPB_TABKEY_STR("\036", "\000", "\000", "\000", "google.protobuf.SourceCodeInfo"), UPB_TABVALUE_PTR_INIT(&msgs[18]), NULL}, - {UPB_TABKEY_STR("\051", "\000", "\000", "\000", "google.protobuf.FieldDescriptorProto.Type"), UPB_TABVALUE_PTR_INIT(&enums[1]), NULL}, - {UPB_TABKEY_STR("\056", "\000", "\000", "\000", "google.protobuf.DescriptorProto.ExtensionRange"), UPB_TABVALUE_PTR_INIT(&msgs[1]), NULL}, - {UPB_TABKEY_STR("\044", "\000", "\000", "\000", "google.protobuf.OneofDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[15]), NULL}, - {UPB_TABKEY_STR("\046", "\000", "\000", "\000", "google.protobuf.ServiceDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[16]), NULL}, - {UPB_TABKEY_STR("\034", "\000", "\000", "\000", "google.protobuf.FieldOptions"), UPB_TABVALUE_PTR_INIT(&msgs[8]), NULL}, - {UPB_TABKEY_STR("\033", "\000", "\000", "\000", "google.protobuf.FileOptions"), UPB_TABVALUE_PTR_INIT(&msgs[11]), NULL}, - {UPB_TABKEY_STR("\043", "\000", "\000", "\000", "google.protobuf.EnumDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[3]), &strentries[265]}, - {UPB_TABKEY_STR("\052", "\000", "\000", "\000", "google.protobuf.FieldDescriptorProto.Label"), UPB_TABVALUE_PTR_INIT(&enums[0]), NULL}, - {UPB_TABKEY_STR("\050", "\000", "\000", "\000", "google.protobuf.FileOptions.OptimizeMode"), UPB_TABVALUE_PTR_INIT(&enums[4]), NULL}, - {UPB_TABKEY_STR("\042", "\000", "\000", "\000", "google.protobuf.FieldOptions.CType"), UPB_TABVALUE_PTR_INIT(&enums[2]), &strentries[261]}, - {UPB_TABKEY_STR("\043", "\000", "\000", "\000", "google.protobuf.FieldOptions.JSType"), UPB_TABVALUE_PTR_INIT(&enums[3]), NULL}, - {UPB_TABKEY_STR("\033", "\000", "\000", "\000", "google.protobuf.EnumOptions"), UPB_TABVALUE_PTR_INIT(&msgs[4]), NULL}, - {UPB_TABKEY_STR("\044", "\000", "\000", "\000", "google.protobuf.FieldDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[7]), NULL}, - {UPB_TABKEY_STR("\050", "\000", "\000", "\000", "google.protobuf.EnumValueDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[5]), &strentries[258]}, - {UPB_TABKEY_STR("\036", "\000", "\000", "\000", "google.protobuf.ServiceOptions"), UPB_TABVALUE_PTR_INIT(&msgs[17]), NULL}, - {UPB_TABKEY_STR("\036", "\000", "\000", "\000", "google.protobuf.MessageOptions"), UPB_TABVALUE_PTR_INIT(&msgs[12]), NULL}, - {UPB_TABKEY_STR("\035", "\000", "\000", "\000", "google.protobuf.MethodOptions"), UPB_TABVALUE_PTR_INIT(&msgs[14]), &strentries[253]}, - {UPB_TABKEY_STR("\054", "\000", "\000", "\000", "google.protobuf.UninterpretedOption.NamePart"), UPB_TABVALUE_PTR_INIT(&msgs[21]), NULL}, }; static const upb_tabent intentries[18] = { {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[100]), NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[101]), NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[98]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[97]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[99]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[95]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[96]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_NUM(33), UPB_TABVALUE_PTR_INIT(&fields[13]), NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[98]), NULL}, - {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, {UPB_TABKEY_NUM(33), UPB_TABVALUE_PTR_INIT(&fields[11]), NULL}, {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, - {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[95]), NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[100]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(33), UPB_TABVALUE_PTR_INIT(&fields[14]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[101]), NULL}, }; static const upb_tabval arrays[184] = { @@ -5487,48 +5899,48 @@ static const upb_tabval arrays[184] = { UPB_TABVALUE_PTR_INIT(&fields[52]), UPB_TABVALUE_PTR_INIT(&fields[25]), UPB_TABVALUE_PTR_INIT(&fields[60]), - UPB_TABVALUE_PTR_INIT(&fields[20]), + UPB_TABVALUE_PTR_INIT(&fields[19]), UPB_TABVALUE_PTR_INIT(&fields[24]), UPB_TABVALUE_PTR_INIT(&fields[22]), - UPB_TABVALUE_PTR_INIT(&fields[70]), + UPB_TABVALUE_PTR_INIT(&fields[68]), UPB_TABVALUE_PTR_INIT(&fields[65]), UPB_TABVALUE_PTR_INIT(&fields[83]), UPB_TABVALUE_PTR_INIT(&fields[82]), UPB_TABVALUE_EMPTY_INIT, - UPB_TABVALUE_PTR_INIT(&fields[88]), + UPB_TABVALUE_PTR_INIT(&fields[89]), UPB_TABVALUE_PTR_INIT(&fields[18]), UPB_TABVALUE_EMPTY_INIT, - UPB_TABVALUE_PTR_INIT(&fields[89]), + UPB_TABVALUE_PTR_INIT(&fields[88]), UPB_TABVALUE_PTR_INIT(&fields[17]), UPB_TABVALUE_EMPTY_INIT, - UPB_TABVALUE_PTR_INIT(&fields[53]), + UPB_TABVALUE_PTR_INIT(&fields[49]), UPB_TABVALUE_PTR_INIT(&fields[102]), - UPB_TABVALUE_PTR_INIT(&fields[69]), + UPB_TABVALUE_PTR_INIT(&fields[74]), UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_PTR_INIT(&fields[1]), - UPB_TABVALUE_PTR_INIT(&fields[10]), + UPB_TABVALUE_PTR_INIT(&fields[13]), UPB_TABVALUE_EMPTY_INIT, - UPB_TABVALUE_PTR_INIT(&fields[50]), - UPB_TABVALUE_PTR_INIT(&fields[63]), - UPB_TABVALUE_PTR_INIT(&fields[71]), - UPB_TABVALUE_EMPTY_INIT, - UPB_TABVALUE_PTR_INIT(&fields[9]), - UPB_TABVALUE_EMPTY_INIT, - UPB_TABVALUE_PTR_INIT(&fields[56]), - UPB_TABVALUE_PTR_INIT(&fields[21]), + UPB_TABVALUE_PTR_INIT(&fields[53]), UPB_TABVALUE_PTR_INIT(&fields[62]), + UPB_TABVALUE_PTR_INIT(&fields[73]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[15]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[55]), + UPB_TABVALUE_PTR_INIT(&fields[21]), + UPB_TABVALUE_PTR_INIT(&fields[63]), UPB_TABVALUE_PTR_INIT(&fields[40]), UPB_TABVALUE_PTR_INIT(&fields[93]), UPB_TABVALUE_PTR_INIT(&fields[94]), UPB_TABVALUE_PTR_INIT(&fields[7]), - UPB_TABVALUE_PTR_INIT(&fields[74]), + UPB_TABVALUE_PTR_INIT(&fields[71]), UPB_TABVALUE_PTR_INIT(&fields[66]), UPB_TABVALUE_PTR_INIT(&fields[38]), UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_PTR_INIT(&fields[6]), UPB_TABVALUE_PTR_INIT(&fields[77]), - UPB_TABVALUE_PTR_INIT(&fields[12]), + UPB_TABVALUE_PTR_INIT(&fields[10]), UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_PTR_INIT(&fields[41]), UPB_TABVALUE_PTR_INIT(&fields[39]), @@ -5537,14 +5949,14 @@ static const upb_tabval arrays[184] = { UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_PTR_INIT(&fields[103]), UPB_TABVALUE_EMPTY_INIT, - UPB_TABVALUE_PTR_INIT(&fields[49]), + UPB_TABVALUE_PTR_INIT(&fields[54]), UPB_TABVALUE_PTR_INIT(&fields[76]), UPB_TABVALUE_PTR_INIT(&fields[8]), UPB_TABVALUE_PTR_INIT(&fields[47]), - UPB_TABVALUE_PTR_INIT(&fields[19]), + UPB_TABVALUE_PTR_INIT(&fields[20]), UPB_TABVALUE_PTR_INIT(&fields[85]), UPB_TABVALUE_PTR_INIT(&fields[23]), - UPB_TABVALUE_PTR_INIT(&fields[72]), + UPB_TABVALUE_PTR_INIT(&fields[69]), UPB_TABVALUE_PTR_INIT(&fields[86]), UPB_TABVALUE_PTR_INIT(&fields[80]), UPB_TABVALUE_PTR_INIT(&fields[104]), @@ -5574,7 +5986,7 @@ static const upb_tabval arrays[184] = { UPB_TABVALUE_PTR_INIT(&fields[31]), UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_EMPTY_INIT, - UPB_TABVALUE_PTR_INIT(&fields[15]), + UPB_TABVALUE_PTR_INIT(&fields[12]), UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_EMPTY_INIT, @@ -5593,25 +6005,25 @@ static const upb_tabval arrays[184] = { UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_PTR_INIT(&fields[46]), UPB_TABVALUE_PTR_INIT(&fields[61]), - UPB_TABVALUE_PTR_INIT(&fields[14]), + UPB_TABVALUE_PTR_INIT(&fields[9]), UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_PTR_INIT(&fields[45]), UPB_TABVALUE_EMPTY_INIT, - UPB_TABVALUE_PTR_INIT(&fields[51]), + UPB_TABVALUE_PTR_INIT(&fields[56]), UPB_TABVALUE_PTR_INIT(&fields[29]), UPB_TABVALUE_PTR_INIT(&fields[75]), - UPB_TABVALUE_PTR_INIT(&fields[68]), + UPB_TABVALUE_PTR_INIT(&fields[70]), UPB_TABVALUE_PTR_INIT(&fields[4]), UPB_TABVALUE_PTR_INIT(&fields[84]), UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_EMPTY_INIT, - UPB_TABVALUE_PTR_INIT(&fields[54]), + UPB_TABVALUE_PTR_INIT(&fields[50]), UPB_TABVALUE_EMPTY_INIT, - UPB_TABVALUE_PTR_INIT(&fields[55]), + UPB_TABVALUE_PTR_INIT(&fields[57]), UPB_TABVALUE_PTR_INIT(&fields[48]), - UPB_TABVALUE_PTR_INIT(&fields[73]), + UPB_TABVALUE_PTR_INIT(&fields[72]), UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_PTR_INIT(&fields[44]), @@ -5624,7 +6036,7 @@ static const upb_tabval arrays[184] = { UPB_TABVALUE_PTR_INIT(&fields[43]), UPB_TABVALUE_EMPTY_INIT, UPB_TABVALUE_EMPTY_INIT, - UPB_TABVALUE_PTR_INIT(&fields[57]), + UPB_TABVALUE_PTR_INIT(&fields[51]), UPB_TABVALUE_PTR_INIT(&fields[28]), UPB_TABVALUE_PTR_INIT(&fields[79]), UPB_TABVALUE_PTR_INIT(&fields[59]), @@ -5669,17 +6081,8 @@ static const upb_tabval arrays[184] = { UPB_TABVALUE_PTR_INIT("LITE_RUNTIME"), }; -static const upb_symtab symtab = UPB_SYMTAB_INIT(UPB_STRTABLE_INIT(27, 31, UPB_CTYPE_PTR, 5, &strentries[236]), &reftables[264], &reftables[265]); - -const upb_symtab *upbdefs_google_protobuf_descriptor(const void *owner) { - upb_symtab_ref(&symtab, owner); - return &symtab; -} - #ifdef UPB_DEBUG_REFS -static upb_inttable reftables[266] = { - UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), - UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), +static upb_inttable reftables[264] = { UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), UPB_EMPTY_INTTABLE_INIT(UPB_CTYPE_PTR), @@ -5947,6 +6350,45 @@ static upb_inttable reftables[266] = { }; #endif +static const upb_msgdef *refm(const upb_msgdef *m, const void *owner) { + upb_msgdef_ref(m, owner); + return m; +} + +static const upb_enumdef *refe(const upb_enumdef *e, const void *owner) { + upb_enumdef_ref(e, owner); + return e; +} + +/* Public API. */ +const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_get(const void *owner) { return refm(&msgs[0], owner); } +const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_get(const void *owner) { return refm(&msgs[1], owner); } +const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ReservedRange_get(const void *owner) { return refm(&msgs[2], owner); } +const upb_msgdef *upbdefs_google_protobuf_EnumDescriptorProto_get(const void *owner) { return refm(&msgs[3], owner); } +const upb_msgdef *upbdefs_google_protobuf_EnumOptions_get(const void *owner) { return refm(&msgs[4], owner); } +const upb_msgdef *upbdefs_google_protobuf_EnumValueDescriptorProto_get(const void *owner) { return refm(&msgs[5], owner); } +const upb_msgdef *upbdefs_google_protobuf_EnumValueOptions_get(const void *owner) { return refm(&msgs[6], owner); } +const upb_msgdef *upbdefs_google_protobuf_FieldDescriptorProto_get(const void *owner) { return refm(&msgs[7], owner); } +const upb_msgdef *upbdefs_google_protobuf_FieldOptions_get(const void *owner) { return refm(&msgs[8], owner); } +const upb_msgdef *upbdefs_google_protobuf_FileDescriptorProto_get(const void *owner) { return refm(&msgs[9], owner); } +const upb_msgdef *upbdefs_google_protobuf_FileDescriptorSet_get(const void *owner) { return refm(&msgs[10], owner); } +const upb_msgdef *upbdefs_google_protobuf_FileOptions_get(const void *owner) { return refm(&msgs[11], owner); } +const upb_msgdef *upbdefs_google_protobuf_MessageOptions_get(const void *owner) { return refm(&msgs[12], owner); } +const upb_msgdef *upbdefs_google_protobuf_MethodDescriptorProto_get(const void *owner) { return refm(&msgs[13], owner); } +const upb_msgdef *upbdefs_google_protobuf_MethodOptions_get(const void *owner) { return refm(&msgs[14], owner); } +const upb_msgdef *upbdefs_google_protobuf_OneofDescriptorProto_get(const void *owner) { return refm(&msgs[15], owner); } +const upb_msgdef *upbdefs_google_protobuf_ServiceDescriptorProto_get(const void *owner) { return refm(&msgs[16], owner); } +const upb_msgdef *upbdefs_google_protobuf_ServiceOptions_get(const void *owner) { return refm(&msgs[17], owner); } +const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo_get(const void *owner) { return refm(&msgs[18], owner); } +const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo_Location_get(const void *owner) { return refm(&msgs[19], owner); } +const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption_get(const void *owner) { return refm(&msgs[20], owner); } +const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption_NamePart_get(const void *owner) { return refm(&msgs[21], owner); } + +const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Label_get(const void *owner) { return refe(&enums[0], owner); } +const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Type_get(const void *owner) { return refe(&enums[1], owner); } +const upb_enumdef *upbdefs_google_protobuf_FieldOptions_CType_get(const void *owner) { return refe(&enums[2], owner); } +const upb_enumdef *upbdefs_google_protobuf_FieldOptions_JSType_get(const void *owner) { return refe(&enums[3], owner); } +const upb_enumdef *upbdefs_google_protobuf_FileOptions_OptimizeMode_get(const void *owner) { return refe(&enums[4], owner); } /* ** XXX: The routines in this file that consume a string do not currently ** support having the string span buffers. In the future, as upb_sink and @@ -5965,15 +6407,6 @@ static bool upb_streq(const char *str, const char *buf, size_t n) { return strlen(str) == n && memcmp(str, buf, n) == 0; } -/* upb_deflist is an internal-only dynamic array for storing a growing list of - * upb_defs. */ -typedef struct { - upb_def **defs; - size_t len; - size_t size; - bool owned; -} upb_deflist; - /* We keep a stack of all the messages scopes we are currently in, as well as * the top-level file scope. This is necessary to correctly qualify the * definitions that are contained inside. "name" tracks the name of the @@ -5999,13 +6432,11 @@ typedef struct { struct upb_descreader { upb_sink sink; - upb_deflist defs; + upb_inttable files; + upb_filedef *file; /* The last file in files. */ upb_descreader_frame stack[UPB_MAX_MESSAGE_NESTING]; int stack_len; - bool primitives_have_presence; - int file_start; - uint32_t number; char *name; bool saw_number; @@ -6017,7 +6448,7 @@ struct upb_descreader { }; static char *upb_strndup(const char *buf, size_t n) { - char *ret = malloc(n + 1); + char *ret = upb_gmalloc(n + 1); if (!ret) return NULL; memcpy(ret, buf, n); ret[n] = '\0'; @@ -6031,9 +6462,12 @@ static char *upb_strndup(const char *buf, size_t n) { * Caller owns a ref on the returned string. */ static char *upb_join(const char *base, const char *name) { if (!base || strlen(base) == 0) { - return upb_strdup(name); + return upb_gstrdup(name); } else { - char *ret = malloc(strlen(base) + strlen(name) + 2); + char *ret = upb_gmalloc(strlen(base) + strlen(name) + 2); + if (!ret) { + return NULL; + } ret[0] = '\0'; strcat(ret, base); strcat(ret, "."); @@ -6042,57 +6476,21 @@ static char *upb_join(const char *base, const char *name) { } } - -/* upb_deflist ****************************************************************/ - -void upb_deflist_init(upb_deflist *l) { - l->size = 0; - l->defs = NULL; - l->len = 0; - l->owned = true; -} - -void upb_deflist_uninit(upb_deflist *l) { - size_t i; - if (l->owned) - for(i = 0; i < l->len; i++) - upb_def_unref(l->defs[i], l); - free(l->defs); -} - -bool upb_deflist_push(upb_deflist *l, upb_def *d) { - if(++l->len >= l->size) { - size_t new_size = UPB_MAX(l->size, 4); - new_size *= 2; - l->defs = realloc(l->defs, new_size * sizeof(void *)); - if (!l->defs) return false; - l->size = new_size; - } - l->defs[l->len - 1] = d; - return true; -} - -void upb_deflist_donaterefs(upb_deflist *l, void *owner) { - size_t i; - assert(l->owned); - for (i = 0; i < l->len; i++) - upb_def_donateref(l->defs[i], l, owner); - l->owned = false; -} - -static upb_def *upb_deflist_last(upb_deflist *l) { - return l->defs[l->len-1]; -} - /* Qualify the defname for all defs starting with offset "start" with "str". */ -static void upb_deflist_qualify(upb_deflist *l, char *str, int32_t start) { - uint32_t i; - for (i = start; i < l->len; i++) { - upb_def *def = l->defs[i]; +static bool upb_descreader_qualify(upb_filedef *f, char *str, int32_t start) { + size_t i; + for (i = start; i < upb_filedef_defcount(f); i++) { + upb_def *def = upb_filedef_mutabledef(f, i); char *name = upb_join(str, upb_def_fullname(def)); + if (!name) { + /* Need better logic here; at this point we've qualified some names but + * not others. */ + return false; + } upb_def_setfullname(def, name, NULL); - free(name); + upb_gfree(name); } + return true; } @@ -6103,92 +6501,144 @@ static upb_msgdef *upb_descreader_top(upb_descreader *r) { assert(r->stack_len > 1); index = r->stack[r->stack_len-1].start - 1; assert(index >= 0); - return upb_downcast_msgdef_mutable(r->defs.defs[index]); + return upb_downcast_msgdef_mutable(upb_filedef_mutabledef(r->file, index)); } static upb_def *upb_descreader_last(upb_descreader *r) { - return upb_deflist_last(&r->defs); + return upb_filedef_mutabledef(r->file, upb_filedef_defcount(r->file) - 1); } /* Start/end handlers for FileDescriptorProto and DescriptorProto (the two * entities that have names and can contain sub-definitions. */ void upb_descreader_startcontainer(upb_descreader *r) { upb_descreader_frame *f = &r->stack[r->stack_len++]; - f->start = r->defs.len; + f->start = upb_filedef_defcount(r->file); f->name = NULL; } -void upb_descreader_endcontainer(upb_descreader *r) { +bool upb_descreader_endcontainer(upb_descreader *r) { upb_descreader_frame *f = &r->stack[--r->stack_len]; - upb_deflist_qualify(&r->defs, f->name, f->start); - free(f->name); + if (!upb_descreader_qualify(r->file, f->name, f->start)) { + return false; + } + upb_gfree(f->name); f->name = NULL; + return true; } void upb_descreader_setscopename(upb_descreader *r, char *str) { upb_descreader_frame *f = &r->stack[r->stack_len-1]; - free(f->name); + upb_gfree(f->name); f->name = str; } -/* Handlers for google.protobuf.FileDescriptorProto. */ -static bool file_startmsg(void *closure, const void *hd) { +/** Handlers for google.protobuf.FileDescriptorSet. ***************************/ + +static void *fileset_startfile(void *closure, const void *hd) { + upb_descreader *r = closure; + UPB_UNUSED(hd); + r->file = upb_filedef_new(&r->files); + upb_inttable_push(&r->files, upb_value_ptr(r->file)); + return r; +} + +/** Handlers for google.protobuf.FileDescriptorProto. *************************/ + +static bool file_start(void *closure, const void *hd) { upb_descreader *r = closure; UPB_UNUSED(hd); upb_descreader_startcontainer(r); - r->primitives_have_presence = true; - r->file_start = r->defs.len; return true; } -static bool file_endmsg(void *closure, const void *hd, upb_status *status) { +static bool file_end(void *closure, const void *hd, upb_status *status) { upb_descreader *r = closure; UPB_UNUSED(hd); UPB_UNUSED(status); - upb_descreader_endcontainer(r); - return true; + return upb_descreader_endcontainer(r); +} + +static size_t file_onname(void *closure, const void *hd, const char *buf, + size_t n, const upb_bufhandle *handle) { + upb_descreader *r = closure; + char *name; + bool ok; + UPB_UNUSED(hd); + UPB_UNUSED(handle); + + name = upb_strndup(buf, n); + /* XXX: see comment at the top of the file. */ + ok = upb_filedef_setname(r->file, name, NULL); + upb_gfree(name); + UPB_ASSERT_VAR(ok, ok); + return n; } static size_t file_onpackage(void *closure, const void *hd, const char *buf, size_t n, const upb_bufhandle *handle) { upb_descreader *r = closure; + char *package; + bool ok; UPB_UNUSED(hd); UPB_UNUSED(handle); + + package = upb_strndup(buf, n); /* XXX: see comment at the top of the file. */ - upb_descreader_setscopename(r, upb_strndup(buf, n)); + upb_descreader_setscopename(r, package); + ok = upb_filedef_setpackage(r->file, package, NULL); + UPB_ASSERT_VAR(ok, ok); return n; } static size_t file_onsyntax(void *closure, const void *hd, const char *buf, size_t n, const upb_bufhandle *handle) { upb_descreader *r = closure; + bool ok; UPB_UNUSED(hd); UPB_UNUSED(handle); /* XXX: see comment at the top of the file. */ if (upb_streq("proto2", buf, n)) { - /* Technically we could verify that proto3 hadn't previously been seen. */ + ok = upb_filedef_setsyntax(r->file, UPB_SYNTAX_PROTO2, NULL); } else if (upb_streq("proto3", buf, n)) { - uint32_t i; - /* Update messages created before the syntax was read. */ - for (i = r->file_start; i < r->defs.len; i++) { - upb_msgdef *m = upb_dyncast_msgdef_mutable(r->defs.defs[i]); - if (m) { - upb_msgdef_setprimitiveshavepresence(m, false); - } - } - - /* Set a flag for any future messages that will be created. */ - r->primitives_have_presence = false; + ok = upb_filedef_setsyntax(r->file, UPB_SYNTAX_PROTO3, NULL); } else { - /* Error: neither proto3 nor proto3. - * TODO(haberman): there should be a status object we can report this to. */ - return 0; + ok = false; } + UPB_ASSERT_VAR(ok, ok); return n; } -/* Handlers for google.protobuf.EnumValueDescriptorProto. */ +static void *file_startmsg(void *closure, const void *hd) { + upb_descreader *r = closure; + upb_msgdef *m = upb_msgdef_new(&m); + bool ok = upb_filedef_addmsg(r->file, m, &m, NULL); + UPB_UNUSED(hd); + UPB_ASSERT_VAR(ok, ok); + return r; +} + +static void *file_startenum(void *closure, const void *hd) { + upb_descreader *r = closure; + upb_enumdef *e = upb_enumdef_new(&e); + bool ok = upb_filedef_addenum(r->file, e, &e, NULL); + UPB_UNUSED(hd); + UPB_ASSERT_VAR(ok, ok); + return r; +} + +static void *file_startext(void *closure, const void *hd) { + upb_descreader *r = closure; + bool ok; + r->f = upb_fielddef_new(r); + ok = upb_filedef_addext(r->file, r->f, r, NULL); + UPB_UNUSED(hd); + UPB_ASSERT_VAR(ok, ok); + return r; +} + +/** Handlers for google.protobuf.EnumValueDescriptorProto. *********************/ + static bool enumval_startmsg(void *closure, const void *hd) { upb_descreader *r = closure; UPB_UNUSED(hd); @@ -6203,7 +6653,7 @@ static size_t enumval_onname(void *closure, const void *hd, const char *buf, UPB_UNUSED(hd); UPB_UNUSED(handle); /* XXX: see comment at the top of the file. */ - free(r->name); + upb_gfree(r->name); r->name = upb_strndup(buf, n); r->saw_name = true; return n; @@ -6228,20 +6678,12 @@ static bool enumval_endmsg(void *closure, const void *hd, upb_status *status) { } e = upb_downcast_enumdef_mutable(upb_descreader_last(r)); upb_enumdef_addval(e, r->name, r->number, status); - free(r->name); + upb_gfree(r->name); r->name = NULL; return true; } - -/* Handlers for google.protobuf.EnumDescriptorProto. */ -static bool enum_startmsg(void *closure, const void *hd) { - upb_descreader *r = closure; - UPB_UNUSED(hd); - upb_deflist_push(&r->defs, - upb_enumdef_upcast_mutable(upb_enumdef_new(&r->defs))); - return true; -} +/** Handlers for google.protobuf.EnumDescriptorProto. *************************/ static bool enum_endmsg(void *closure, const void *hd, upb_status *status) { upb_descreader *r = closure; @@ -6268,16 +6710,17 @@ static size_t enum_onname(void *closure, const void *hd, const char *buf, UPB_UNUSED(handle); /* XXX: see comment at the top of the file. */ upb_def_setfullname(upb_descreader_last(r), fullname, NULL); - free(fullname); + upb_gfree(fullname); return n; } -/* Handlers for google.protobuf.FieldDescriptorProto */ +/** Handlers for google.protobuf.FieldDescriptorProto *************************/ + static bool field_startmsg(void *closure, const void *hd) { upb_descreader *r = closure; UPB_UNUSED(hd); - r->f = upb_fielddef_new(&r->defs); - free(r->default_string); + assert(r->f); + upb_gfree(r->default_string); r->default_string = NULL; /* fielddefs default to packed, but descriptors default to non-packed. */ @@ -6419,9 +6862,10 @@ static bool field_onlabel(void *closure, const void *hd, int32_t val) { static bool field_onnumber(void *closure, const void *hd, int32_t val) { upb_descreader *r = closure; - bool ok = upb_fielddef_setnumber(r->f, val, NULL); + bool ok; UPB_UNUSED(hd); + ok = upb_fielddef_setnumber(r->f, val, NULL); UPB_ASSERT_VAR(ok, ok); return true; } @@ -6435,7 +6879,7 @@ static size_t field_onname(void *closure, const void *hd, const char *buf, /* XXX: see comment at the top of the file. */ upb_fielddef_setname(r->f, name, NULL); - free(name); + upb_gfree(name); return n; } @@ -6448,7 +6892,7 @@ static size_t field_ontypename(void *closure, const void *hd, const char *buf, /* XXX: see comment at the top of the file. */ upb_fielddef_setsubdefname(r->f, name, NULL); - free(name); + upb_gfree(name); return n; } @@ -6461,7 +6905,7 @@ static size_t field_onextendee(void *closure, const void *hd, const char *buf, /* XXX: see comment at the top of the file. */ upb_fielddef_setcontainingtypename(r->f, name, NULL); - free(name); + upb_gfree(name); return n; } @@ -6474,25 +6918,22 @@ static size_t field_ondefaultval(void *closure, const void *hd, const char *buf, /* Have to convert from string to the correct type, but we might not know the * type yet, so we save it as a string until the end of the field. * XXX: see comment at the top of the file. */ - free(r->default_string); + upb_gfree(r->default_string); r->default_string = upb_strndup(buf, n); return n; } -/* Handlers for google.protobuf.DescriptorProto (representing a message). */ -static bool msg_startmsg(void *closure, const void *hd) { +/** Handlers for google.protobuf.DescriptorProto ******************************/ + +static bool msg_start(void *closure, const void *hd) { upb_descreader *r = closure; - upb_msgdef *m; UPB_UNUSED(hd); - m = upb_msgdef_new(&r->defs); - upb_msgdef_setprimitiveshavepresence(m, r->primitives_have_presence); - upb_deflist_push(&r->defs, upb_msgdef_upcast_mutable(m)); upb_descreader_startcontainer(r); return true; } -static bool msg_endmsg(void *closure, const void *hd, upb_status *status) { +static bool msg_end(void *closure, const void *hd, upb_status *status) { upb_descreader *r = closure; upb_msgdef *m = upb_descreader_top(r); UPB_UNUSED(hd); @@ -6501,12 +6942,11 @@ static bool msg_endmsg(void *closure, const void *hd, upb_status *status) { upb_status_seterrmsg(status, "Encountered message with no name."); return false; } - upb_descreader_endcontainer(r); - return true; + return upb_descreader_endcontainer(r); } -static size_t msg_onname(void *closure, const void *hd, const char *buf, - size_t n, const upb_bufhandle *handle) { +static size_t msg_name(void *closure, const void *hd, const char *buf, + size_t n, const upb_bufhandle *handle) { upb_descreader *r = closure; upb_msgdef *m = upb_descreader_top(r); /* XXX: see comment at the top of the file. */ @@ -6519,93 +6959,147 @@ static size_t msg_onname(void *closure, const void *hd, const char *buf, return n; } -static bool msg_onendfield(void *closure, const void *hd) { +static void *msg_startmsg(void *closure, const void *hd) { + upb_descreader *r = closure; + upb_msgdef *m = upb_msgdef_new(&m); + bool ok = upb_filedef_addmsg(r->file, m, &m, NULL); + UPB_UNUSED(hd); + UPB_ASSERT_VAR(ok, ok); + return r; +} + +static void *msg_startext(void *closure, const void *hd) { + upb_descreader *r = closure; + upb_fielddef *f = upb_fielddef_new(&f); + bool ok = upb_filedef_addext(r->file, f, &f, NULL); + UPB_UNUSED(hd); + UPB_ASSERT_VAR(ok, ok); + return r; +} + +static void *msg_startfield(void *closure, const void *hd) { + upb_descreader *r = closure; + r->f = upb_fielddef_new(&r->f); + /* We can't add the new field to the message until its name/number are + * filled in. */ + UPB_UNUSED(hd); + return r; +} + +static bool msg_endfield(void *closure, const void *hd) { upb_descreader *r = closure; upb_msgdef *m = upb_descreader_top(r); UPB_UNUSED(hd); - upb_msgdef_addfield(m, r->f, &r->defs, NULL); + upb_msgdef_addfield(m, r->f, &r->f, NULL); r->f = NULL; return true; } -static bool pushextension(void *closure, const void *hd) { +static bool msg_onmapentry(void *closure, const void *hd, bool mapentry) { upb_descreader *r = closure; + upb_msgdef *m = upb_descreader_top(r); UPB_UNUSED(hd); - assert(upb_fielddef_containingtypename(r->f)); - upb_fielddef_setisextension(r->f, true); - upb_deflist_push(&r->defs, upb_fielddef_upcast_mutable(r->f)); + upb_msgdef_setmapentry(m, mapentry); r->f = NULL; return true; } -#define D(name) upbdefs_google_protobuf_ ## name(s) + + +/** Code to register handlers *************************************************/ + +#define F(msg, field) upbdefs_google_protobuf_ ## msg ## _f_ ## field(m) static void reghandlers(const void *closure, upb_handlers *h) { - const upb_symtab *s = closure; const upb_msgdef *m = upb_handlers_msgdef(h); + UPB_UNUSED(closure); - if (m == D(DescriptorProto)) { - upb_handlers_setstartmsg(h, &msg_startmsg, NULL); - upb_handlers_setendmsg(h, &msg_endmsg, NULL); - upb_handlers_setstring(h, D(DescriptorProto_name), &msg_onname, NULL); - upb_handlers_setendsubmsg(h, D(DescriptorProto_field), &msg_onendfield, - NULL); - upb_handlers_setendsubmsg(h, D(DescriptorProto_extension), &pushextension, - NULL); - } else if (m == D(FileDescriptorProto)) { - upb_handlers_setstartmsg(h, &file_startmsg, NULL); - upb_handlers_setendmsg(h, &file_endmsg, NULL); - upb_handlers_setstring(h, D(FileDescriptorProto_package), &file_onpackage, + if (upbdefs_google_protobuf_FileDescriptorSet_is(m)) { + upb_handlers_setstartsubmsg(h, F(FileDescriptorSet, file), + &fileset_startfile, NULL); + } else if (upbdefs_google_protobuf_DescriptorProto_is(m)) { + upb_handlers_setstartmsg(h, &msg_start, NULL); + upb_handlers_setendmsg(h, &msg_end, NULL); + upb_handlers_setstring(h, F(DescriptorProto, name), &msg_name, NULL); + upb_handlers_setstartsubmsg(h, F(DescriptorProto, extension), &msg_startext, + NULL); + upb_handlers_setstartsubmsg(h, F(DescriptorProto, nested_type), + &msg_startmsg, NULL); + upb_handlers_setstartsubmsg(h, F(DescriptorProto, field), + &msg_startfield, NULL); + upb_handlers_setendsubmsg(h, F(DescriptorProto, field), + &msg_endfield, NULL); + upb_handlers_setstartsubmsg(h, F(DescriptorProto, enum_type), + &file_startenum, NULL); + } else if (upbdefs_google_protobuf_FileDescriptorProto_is(m)) { + upb_handlers_setstartmsg(h, &file_start, NULL); + upb_handlers_setendmsg(h, &file_end, NULL); + upb_handlers_setstring(h, F(FileDescriptorProto, name), &file_onname, NULL); - upb_handlers_setstring(h, D(FileDescriptorProto_syntax), &file_onsyntax, + upb_handlers_setstring(h, F(FileDescriptorProto, package), &file_onpackage, NULL); - upb_handlers_setendsubmsg(h, D(FileDescriptorProto_extension), &pushextension, - NULL); - } else if (m == D(EnumValueDescriptorProto)) { + upb_handlers_setstring(h, F(FileDescriptorProto, syntax), &file_onsyntax, + NULL); + upb_handlers_setstartsubmsg(h, F(FileDescriptorProto, message_type), + &file_startmsg, NULL); + upb_handlers_setstartsubmsg(h, F(FileDescriptorProto, enum_type), + &file_startenum, NULL); + upb_handlers_setstartsubmsg(h, F(FileDescriptorProto, extension), + &file_startext, NULL); + } else if (upbdefs_google_protobuf_EnumValueDescriptorProto_is(m)) { upb_handlers_setstartmsg(h, &enumval_startmsg, NULL); upb_handlers_setendmsg(h, &enumval_endmsg, NULL); - upb_handlers_setstring(h, D(EnumValueDescriptorProto_name), &enumval_onname, NULL); - upb_handlers_setint32(h, D(EnumValueDescriptorProto_number), &enumval_onnumber, + upb_handlers_setstring(h, F(EnumValueDescriptorProto, name), &enumval_onname, NULL); + upb_handlers_setint32(h, F(EnumValueDescriptorProto, number), &enumval_onnumber, NULL); - } else if (m == D(EnumDescriptorProto)) { - upb_handlers_setstartmsg(h, &enum_startmsg, NULL); + } else if (upbdefs_google_protobuf_EnumDescriptorProto_is(m)) { upb_handlers_setendmsg(h, &enum_endmsg, NULL); - upb_handlers_setstring(h, D(EnumDescriptorProto_name), &enum_onname, NULL); - } else if (m == D(FieldDescriptorProto)) { + upb_handlers_setstring(h, F(EnumDescriptorProto, name), &enum_onname, NULL); + } else if (upbdefs_google_protobuf_FieldDescriptorProto_is(m)) { upb_handlers_setstartmsg(h, &field_startmsg, NULL); upb_handlers_setendmsg(h, &field_endmsg, NULL); - upb_handlers_setint32(h, D(FieldDescriptorProto_type), &field_ontype, + upb_handlers_setint32(h, F(FieldDescriptorProto, type), &field_ontype, NULL); - upb_handlers_setint32(h, D(FieldDescriptorProto_label), &field_onlabel, + upb_handlers_setint32(h, F(FieldDescriptorProto, label), &field_onlabel, NULL); - upb_handlers_setint32(h, D(FieldDescriptorProto_number), &field_onnumber, + upb_handlers_setint32(h, F(FieldDescriptorProto, number), &field_onnumber, NULL); - upb_handlers_setstring(h, D(FieldDescriptorProto_name), &field_onname, + upb_handlers_setstring(h, F(FieldDescriptorProto, name), &field_onname, NULL); - upb_handlers_setstring(h, D(FieldDescriptorProto_type_name), + upb_handlers_setstring(h, F(FieldDescriptorProto, type_name), &field_ontypename, NULL); - upb_handlers_setstring(h, D(FieldDescriptorProto_extendee), + upb_handlers_setstring(h, F(FieldDescriptorProto, extendee), &field_onextendee, NULL); - upb_handlers_setstring(h, D(FieldDescriptorProto_default_value), + upb_handlers_setstring(h, F(FieldDescriptorProto, default_value), &field_ondefaultval, NULL); - } else if (m == D(FieldOptions)) { - upb_handlers_setbool(h, D(FieldOptions_lazy), &field_onlazy, NULL); - upb_handlers_setbool(h, D(FieldOptions_packed), &field_onpacked, NULL); + } else if (upbdefs_google_protobuf_FieldOptions_is(m)) { + upb_handlers_setbool(h, F(FieldOptions, lazy), &field_onlazy, NULL); + upb_handlers_setbool(h, F(FieldOptions, packed), &field_onpacked, NULL); + } else if (upbdefs_google_protobuf_MessageOptions_is(m)) { + upb_handlers_setbool(h, F(MessageOptions, map_entry), &msg_onmapentry, NULL); } + + assert(upb_ok(upb_handlers_status(h))); } -#undef D +#undef F void descreader_cleanup(void *_r) { upb_descreader *r = _r; - free(r->name); - upb_deflist_uninit(&r->defs); - free(r->default_string); + size_t i; + + for (i = 0; i < upb_descreader_filecount(r); i++) { + upb_filedef_unref(upb_descreader_file(r, i), &r->files); + } + + upb_gfree(r->name); + upb_inttable_uninit(&r->files); + upb_gfree(r->default_string); while (r->stack_len > 0) { upb_descreader_frame *f = &r->stack[--r->stack_len]; - free(f->name); + upb_gfree(f->name); } } @@ -6618,7 +7112,7 @@ upb_descreader *upb_descreader_create(upb_env *e, const upb_handlers *h) { return NULL; } - upb_deflist_init(&r->defs); + upb_inttable_init(&r->files, UPB_CTYPE_PTR); upb_sink_reset(upb_descreader_input(r), h, r); r->stack_len = 0; r->name = NULL; @@ -6627,10 +7121,17 @@ upb_descreader *upb_descreader_create(upb_env *e, const upb_handlers *h) { return r; } -upb_def **upb_descreader_getdefs(upb_descreader *r, void *owner, int *n) { - *n = r->defs.len; - upb_deflist_donaterefs(&r->defs, owner); - return r->defs.defs; +size_t upb_descreader_filecount(const upb_descreader *r) { + return upb_inttable_count(&r->files); +} + +upb_filedef *upb_descreader_file(const upb_descreader *r, size_t i) { + upb_value v; + if (upb_inttable_lookup(&r->files, i, &v)) { + return upb_value_getptr(v); + } else { + return NULL; + } } upb_sink *upb_descreader_input(upb_descreader *r) { @@ -6638,10 +7139,9 @@ upb_sink *upb_descreader_input(upb_descreader *r) { } const upb_handlers *upb_descreader_newhandlers(const void *owner) { - const upb_symtab *s = upbdefs_google_protobuf_descriptor(&s); - const upb_handlers *h = upb_handlers_newfrozen( - upbdefs_google_protobuf_FileDescriptorSet(s), owner, reghandlers, s); - upb_symtab_unref(s, &s); + const upb_msgdef *m = upbdefs_google_protobuf_FileDescriptorSet_get(&m); + const upb_handlers *h = upb_handlers_newfrozen(m, owner, reghandlers, NULL); + upb_msgdef_unref(m, &m); return h; } /* @@ -6675,8 +7175,8 @@ static void freegroup(upb_refcounted *r) { #ifdef UPB_USE_JIT_X64 upb_pbdecoder_freejit(g); #endif - free(g->bytecode); - free(g); + upb_gfree(g->bytecode); + upb_gfree(g); } static void visitgroup(const upb_refcounted *r, upb_refcounted_visit *visit, @@ -6691,7 +7191,7 @@ static void visitgroup(const upb_refcounted *r, upb_refcounted_visit *visit, } mgroup *newgroup(const void *owner) { - mgroup *g = malloc(sizeof(*g)); + mgroup *g = upb_gmalloc(sizeof(*g)); static const struct upb_refcounted_vtbl vtbl = {visitgroup, freegroup}; upb_refcounted_init(mgroup_upcast_mutable(g), &vtbl, owner); upb_inttable_init(&g->methods, UPB_CTYPE_PTR); @@ -6711,7 +7211,7 @@ static void freemethod(upb_refcounted *r) { } upb_inttable_uninit(&method->dispatch); - free(method); + upb_gfree(method); } static void visitmethod(const upb_refcounted *r, upb_refcounted_visit *visit, @@ -6723,7 +7223,7 @@ static void visitmethod(const upb_refcounted *r, upb_refcounted_visit *visit, static upb_pbdecodermethod *newmethod(const upb_handlers *dest_handlers, mgroup *group) { static const struct upb_refcounted_vtbl vtbl = {visitmethod, freemethod}; - upb_pbdecodermethod *ret = malloc(sizeof(*ret)); + upb_pbdecodermethod *ret = upb_gmalloc(sizeof(*ret)); upb_refcounted_init(upb_pbdecodermethod_upcast_mutable(ret), &vtbl, &ret); upb_byteshandler_init(&ret->input_handler_); @@ -6786,7 +7286,7 @@ typedef struct { } compiler; static compiler *newcompiler(mgroup *group, bool lazy) { - compiler *ret = malloc(sizeof(*ret)); + compiler *ret = upb_gmalloc(sizeof(*ret)); int i; ret->group = group; @@ -6799,7 +7299,7 @@ static compiler *newcompiler(mgroup *group, bool lazy) { } static void freecompiler(compiler *c) { - free(c); + upb_gfree(c); } const size_t ptr_words = sizeof(void*) / sizeof(uint32_t); @@ -6903,7 +7403,8 @@ static void put32(compiler *c, uint32_t v) { size_t oldsize = g->bytecode_end - g->bytecode; size_t newsize = UPB_MAX(oldsize * 2, 64); /* TODO(haberman): handle OOM. */ - g->bytecode = realloc(g->bytecode, newsize * sizeof(uint32_t)); + g->bytecode = upb_grealloc(g->bytecode, oldsize * sizeof(uint32_t), + newsize * sizeof(uint32_t)); g->bytecode_end = g->bytecode + newsize; c->pc = g->bytecode + ofs; } @@ -8768,7 +9269,6 @@ bool upb_pbdecoder_setmaxnesting(upb_pbdecoder *d, size_t max) { */ -#include /* The output buffer is divided into segments; a segment is a string of data * that is "ready to go" -- it does not need any varint lengths inserted into @@ -9013,12 +9513,12 @@ static void new_tag(upb_handlers *h, const upb_fielddef *f, upb_wiretype_t wt, upb_handlerattr *attr) { uint32_t n = upb_fielddef_number(f); - tag_t *tag = malloc(sizeof(tag_t)); + tag_t *tag = upb_gmalloc(sizeof(tag_t)); tag->bytes = upb_vencode64((n << 3) | wt, tag->tag); upb_handlerattr_init(attr); upb_handlerattr_sethandlerdata(attr, tag); - upb_handlers_addcleanup(h, tag, free); + upb_handlers_addcleanup(h, tag, upb_gfree); } static bool encode_tag(upb_pb_encoder *e, const tag_t *tag) { @@ -9277,12 +9777,9 @@ upb_pb_encoder *upb_pb_encoder_create(upb_env *env, const upb_handlers *h, upb_sink *upb_pb_encoder_input(upb_pb_encoder *e) { return &e->input_; } -#include -#include -#include -upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n, - void *owner, upb_status *status) { +upb_filedef **upb_loaddescriptor(const char *buf, size_t n, const void *owner, + upb_status *status) { /* Create handlers. */ const upb_pbdecodermethod *decoder_m; const upb_handlers *reader_h = upb_descreader_newhandlers(&reader_h); @@ -9291,8 +9788,8 @@ upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n, upb_pbdecoder *decoder; upb_descreader *reader; bool ok; - upb_def **ret = NULL; - upb_def **defs; + size_t i; + upb_filedef **ret = NULL; upb_pbdecodermethodopts_init(&opts, reader_h); decoder_m = upb_pbdecodermethod_new(&opts, &decoder_m); @@ -9304,12 +9801,24 @@ upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n, decoder = upb_pbdecoder_create(&env, decoder_m, upb_descreader_input(reader)); /* Push input data. */ - ok = upb_bufsrc_putbuf(str, len, upb_pbdecoder_input(decoder)); + ok = upb_bufsrc_putbuf(buf, n, upb_pbdecoder_input(decoder)); - if (!ok) goto cleanup; - defs = upb_descreader_getdefs(reader, owner, n); - ret = malloc(sizeof(upb_def*) * (*n)); - memcpy(ret, defs, sizeof(upb_def*) * (*n)); + if (!ok) { + goto cleanup; + } + + ret = upb_gmalloc(sizeof (*ret) * (upb_descreader_filecount(reader) + 1)); + + if (!ret) { + goto cleanup; + } + + for (i = 0; i < upb_descreader_filecount(reader); i++) { + ret[i] = upb_descreader_file(reader, i); + upb_filedef_ref(ret[i], owner); + } + + ret[i] = NULL; cleanup: upb_env_uninit(&env); @@ -9317,51 +9826,6 @@ cleanup: upb_pbdecodermethod_unref(decoder_m, &decoder_m); return ret; } - -bool upb_load_descriptor_into_symtab(upb_symtab *s, const char *str, size_t len, - upb_status *status) { - int n; - bool success; - upb_def **defs = upb_load_defs_from_descriptor(str, len, &n, &defs, status); - if (!defs) return false; - success = upb_symtab_add(s, defs, n, &defs, status); - free(defs); - return success; -} - -char *upb_readfile(const char *filename, size_t *len) { - long size; - char *buf; - FILE *f = fopen(filename, "rb"); - if(!f) return NULL; - if(fseek(f, 0, SEEK_END) != 0) goto error; - size = ftell(f); - if(size < 0) goto error; - if(fseek(f, 0, SEEK_SET) != 0) goto error; - buf = malloc(size + 1); - if(size && fread(buf, size, 1, f) != 1) goto error; - fclose(f); - if (len) *len = size; - return buf; - -error: - fclose(f); - return NULL; -} - -bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname, - upb_status *status) { - size_t len; - bool success; - char *data = upb_readfile(fname, &len); - if (!data) { - if (status) upb_status_seterrf(status, "Couldn't read file: %s", fname); - return false; - } - success = upb_load_descriptor_into_symtab(symtab, data, len, status); - free(data); - return success; -} /* * upb::pb::TextPrinter * @@ -9375,7 +9839,6 @@ bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname, #include #include #include -#include #include @@ -9471,14 +9934,14 @@ bool putf(upb_textprinter *p, const char *fmt, ...) { va_end(args_copy); /* + 1 for NULL terminator (vsprintf() requires it even if we don't). */ - str = malloc(len + 1); + str = upb_gmalloc(len + 1); if (!str) return false; written = vsprintf(str, fmt, args); va_end(args); UPB_ASSERT_VAR(written, written == len); ok = upb_bytessink_putbuf(p->output_, p->subc, str, len, NULL); - free(str); + upb_gfree(str); return ok; } @@ -9847,12 +10310,11 @@ upb_decoderet upb_vdecode_max8_wright(upb_decoderet r) { ** - handling of keys/escape-sequences/etc that span input buffers. */ -#include -#include #include -#include -#include #include +#include +#include +#include #define UPB_JSON_MAX_DEPTH 64 @@ -10975,11 +11437,11 @@ static void end_object(upb_json_parser *p) { * final state once, when the closing '"' is seen. */ -#line 1246 "upb/json/parser.rl" +#line 1245 "upb/json/parser.rl" -#line 1158 "upb/json/parser.c" +#line 1157 "upb/json/parser.c" static const char _json_actions[] = { 0, 1, 0, 1, 2, 1, 3, 1, 5, 1, 6, 1, 7, 1, 8, 1, @@ -11128,7 +11590,7 @@ static const int json_en_value_machine = 27; static const int json_en_main = 1; -#line 1249 "upb/json/parser.rl" +#line 1248 "upb/json/parser.rl" size_t parse(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle) { @@ -11150,7 +11612,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, capture_resume(parser, buf); -#line 1329 "upb/json/parser.c" +#line 1328 "upb/json/parser.c" { int _klen; unsigned int _trans; @@ -11225,118 +11687,118 @@ _match: switch ( *_acts++ ) { case 0: -#line 1161 "upb/json/parser.rl" +#line 1160 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 1: -#line 1162 "upb/json/parser.rl" +#line 1161 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 10; goto _again;} } break; case 2: -#line 1166 "upb/json/parser.rl" +#line 1165 "upb/json/parser.rl" { start_text(parser, p); } break; case 3: -#line 1167 "upb/json/parser.rl" +#line 1166 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_text(parser, p)); } break; case 4: -#line 1173 "upb/json/parser.rl" +#line 1172 "upb/json/parser.rl" { start_hex(parser); } break; case 5: -#line 1174 "upb/json/parser.rl" +#line 1173 "upb/json/parser.rl" { hexdigit(parser, p); } break; case 6: -#line 1175 "upb/json/parser.rl" +#line 1174 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hex(parser)); } break; case 7: -#line 1181 "upb/json/parser.rl" +#line 1180 "upb/json/parser.rl" { CHECK_RETURN_TOP(escape(parser, p)); } break; case 8: -#line 1187 "upb/json/parser.rl" +#line 1186 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 9: -#line 1190 "upb/json/parser.rl" +#line 1189 "upb/json/parser.rl" { {stack[top++] = cs; cs = 19; goto _again;} } break; case 10: -#line 1192 "upb/json/parser.rl" +#line 1191 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 27; goto _again;} } break; case 11: -#line 1197 "upb/json/parser.rl" +#line 1196 "upb/json/parser.rl" { start_member(parser); } break; case 12: -#line 1198 "upb/json/parser.rl" +#line 1197 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_membername(parser)); } break; case 13: -#line 1201 "upb/json/parser.rl" +#line 1200 "upb/json/parser.rl" { end_member(parser); } break; case 14: -#line 1207 "upb/json/parser.rl" +#line 1206 "upb/json/parser.rl" { start_object(parser); } break; case 15: -#line 1210 "upb/json/parser.rl" +#line 1209 "upb/json/parser.rl" { end_object(parser); } break; case 16: -#line 1216 "upb/json/parser.rl" +#line 1215 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_array(parser)); } break; case 17: -#line 1220 "upb/json/parser.rl" +#line 1219 "upb/json/parser.rl" { end_array(parser); } break; case 18: -#line 1225 "upb/json/parser.rl" +#line 1224 "upb/json/parser.rl" { start_number(parser, p); } break; case 19: -#line 1226 "upb/json/parser.rl" +#line 1225 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 20: -#line 1228 "upb/json/parser.rl" +#line 1227 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_stringval(parser)); } break; case 21: -#line 1229 "upb/json/parser.rl" +#line 1228 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_stringval(parser)); } break; case 22: -#line 1231 "upb/json/parser.rl" +#line 1230 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, true)); } break; case 23: -#line 1233 "upb/json/parser.rl" +#line 1232 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, false)); } break; case 24: -#line 1235 "upb/json/parser.rl" +#line 1234 "upb/json/parser.rl" { /* null value */ } break; case 25: -#line 1237 "upb/json/parser.rl" +#line 1236 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_subobject(parser)); } break; case 26: -#line 1238 "upb/json/parser.rl" +#line 1237 "upb/json/parser.rl" { end_subobject(parser); } break; case 27: -#line 1243 "upb/json/parser.rl" +#line 1242 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; -#line 1515 "upb/json/parser.c" +#line 1514 "upb/json/parser.c" } } @@ -11349,10 +11811,10 @@ _again: _out: {} } -#line 1270 "upb/json/parser.rl" +#line 1269 "upb/json/parser.rl" if (p != pe) { - upb_status_seterrf(&parser->status, "Parse error at %s\n", p); + upb_status_seterrf(&parser->status, "Parse error at '%.*s'\n", pe - p, p); upb_env_reporterror(parser->env, &parser->status); } else { capture_suspend(parser, &p); @@ -11390,13 +11852,13 @@ static void json_parser_reset(upb_json_parser *p) { /* Emit Ragel initialization of the parser. */ -#line 1569 "upb/json/parser.c" +#line 1568 "upb/json/parser.c" { cs = json_start; top = 0; } -#line 1310 "upb/json/parser.rl" +#line 1309 "upb/json/parser.rl" p->current_state = cs; p->parser_top = top; accumulate_clear(p); @@ -11422,12 +11884,12 @@ static void free_json_parsermethod(upb_refcounted *r) { upb_value val = upb_inttable_iter_value(&i); upb_strtable *t = upb_value_getptr(val); upb_strtable_uninit(t); - free(t); + upb_gfree(t); } upb_inttable_uninit(&method->name_tables); - free(r); + upb_gfree(r); } static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) { @@ -11444,7 +11906,7 @@ static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) { } /* TODO(haberman): handle malloc failure. */ - t = malloc(sizeof(*t)); + t = upb_gmalloc(sizeof(*t)); upb_strtable_init(t, UPB_CTYPE_CONSTPTR); upb_inttable_insertptr(&m->name_tables, md, upb_value_ptr(t)); @@ -11452,20 +11914,31 @@ static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) { !upb_msg_field_done(&i); upb_msg_field_next(&i)) { const upb_fielddef *f = upb_msg_iter_field(&i); + + /* Add an entry for the JSON name. */ size_t field_len = upb_fielddef_getjsonname(f, buf, len); if (field_len > len) { - buf = realloc(buf, field_len); + size_t len2; + buf = upb_grealloc(buf, 0, field_len); len = field_len; - upb_fielddef_getjsonname(f, buf, len); + len2 = upb_fielddef_getjsonname(f, buf, len); + UPB_ASSERT_VAR(len2, len == len2); } upb_strtable_insert(t, buf, upb_value_constptr(f)); + if (strcmp(buf, upb_fielddef_name(f)) != 0) { + /* Since the JSON name is different from the regular field name, add an + * entry for the raw name (compliant proto3 JSON parsers must accept + * both). */ + upb_strtable_insert(t, upb_fielddef_name(f), upb_value_constptr(f)); + } + if (upb_fielddef_issubmsg(f)) { add_jsonname_table(m, upb_fielddef_msgsubdef(f)); } } - free(buf); + upb_gfree(buf); } /* Public API *****************************************************************/ @@ -11505,7 +11978,7 @@ upb_json_parsermethod *upb_json_parsermethod_new(const upb_msgdef* md, const void* owner) { static const struct upb_refcounted_vtbl vtbl = {visit_json_parsermethod, free_json_parsermethod}; - upb_json_parsermethod *ret = malloc(sizeof(*ret)); + upb_json_parsermethod *ret = upb_gmalloc(sizeof(*ret)); upb_refcounted_init(upb_json_parsermethod_upcast_mutable(ret), &vtbl, owner); ret->msg = md; @@ -11532,8 +12005,6 @@ const upb_byteshandler *upb_json_parsermethod_inputhandler( */ -#include -#include #include #include @@ -11566,20 +12037,26 @@ typedef struct { void freestrpc(void *ptr) { strpc *pc = ptr; - free(pc->ptr); - free(pc); + upb_gfree(pc->ptr); + upb_gfree(pc); } /* Convert fielddef name to JSON name and return as a string piece. */ -strpc *newstrpc(upb_handlers *h, const upb_fielddef *f) { +strpc *newstrpc(upb_handlers *h, const upb_fielddef *f, + bool preserve_fieldnames) { /* TODO(haberman): handle malloc failure. */ - strpc *ret = malloc(sizeof(*ret)); - size_t len; - ret->len = upb_fielddef_getjsonname(f, NULL, 0); - ret->ptr = malloc(ret->len); - len = upb_fielddef_getjsonname(f, ret->ptr, ret->len); - UPB_ASSERT_VAR(len, len == ret->len); - ret->len--; /* NULL */ + strpc *ret = upb_gmalloc(sizeof(*ret)); + if (preserve_fieldnames) { + ret->ptr = upb_gstrdup(upb_fielddef_name(f)); + ret->len = strlen(ret->ptr); + } else { + size_t len; + ret->len = upb_fielddef_getjsonname(f, NULL, 0); + ret->ptr = upb_gmalloc(ret->len); + len = upb_fielddef_getjsonname(f, ret->ptr, ret->len); + UPB_ASSERT_VAR(len, len == ret->len); + ret->len--; /* NULL */ + } upb_handlers_addcleanup(h, ret, freestrpc); return ret; @@ -12085,11 +12562,12 @@ static size_t mapkey_bytes(void *closure, const void *handler_data, static void set_enum_hd(upb_handlers *h, const upb_fielddef *f, + bool preserve_fieldnames, upb_handlerattr *attr) { - EnumHandlerData *hd = malloc(sizeof(EnumHandlerData)); + EnumHandlerData *hd = upb_gmalloc(sizeof(EnumHandlerData)); hd->enumdef = (const upb_enumdef *)upb_fielddef_subdef(f); - hd->keyname = newstrpc(h, f); - upb_handlers_addcleanup(h, hd, free); + hd->keyname = newstrpc(h, f, preserve_fieldnames); + upb_handlers_addcleanup(h, hd, upb_gfree); upb_handlerattr_sethandlerdata(attr, hd); } @@ -12105,7 +12583,8 @@ static void set_enum_hd(upb_handlers *h, * our sources that emit mapentry messages do so canonically (with one key * field, and then one value field), so this is not a pressing concern at the * moment. */ -void printer_sethandlers_mapentry(const void *closure, upb_handlers *h) { +void printer_sethandlers_mapentry(const void *closure, bool preserve_fieldnames, + upb_handlers *h) { const upb_msgdef *md = upb_handlers_msgdef(h); /* A mapentry message is printed simply as '"key": value'. Rather than @@ -12179,7 +12658,7 @@ void printer_sethandlers_mapentry(const void *closure, upb_handlers *h) { break; case UPB_TYPE_ENUM: { upb_handlerattr enum_attr = UPB_HANDLERATTR_INITIALIZER; - set_enum_hd(h, value_field, &enum_attr); + set_enum_hd(h, value_field, preserve_fieldnames, &enum_attr); upb_handlers_setint32(h, value_field, mapvalue_enum, &enum_attr); upb_handlerattr_uninit(&enum_attr); break; @@ -12198,13 +12677,13 @@ void printer_sethandlers(const void *closure, upb_handlers *h) { bool is_mapentry = upb_msgdef_mapentry(md); upb_handlerattr empty_attr = UPB_HANDLERATTR_INITIALIZER; upb_msg_field_iter i; - - UPB_UNUSED(closure); + const bool *preserve_fieldnames_ptr = closure; + const bool preserve_fieldnames = *preserve_fieldnames_ptr; if (is_mapentry) { /* mapentry messages are sufficiently different that we handle them * separately. */ - printer_sethandlers_mapentry(closure, h); + printer_sethandlers_mapentry(closure, preserve_fieldnames, h); return; } @@ -12225,7 +12704,8 @@ void printer_sethandlers(const void *closure, upb_handlers *h) { const upb_fielddef *f = upb_msg_iter_field(&i); upb_handlerattr name_attr = UPB_HANDLERATTR_INITIALIZER; - upb_handlerattr_sethandlerdata(&name_attr, newstrpc(h, f)); + upb_handlerattr_sethandlerdata(&name_attr, + newstrpc(h, f, preserve_fieldnames)); if (upb_fielddef_ismap(f)) { upb_handlers_setstartseq(h, f, startmap, &name_attr); @@ -12248,7 +12728,7 @@ void printer_sethandlers(const void *closure, upb_handlers *h) { * option later to control this behavior, but we will wait for a real * need first. */ upb_handlerattr enum_attr = UPB_HANDLERATTR_INITIALIZER; - set_enum_hd(h, f, &enum_attr); + set_enum_hd(h, f, preserve_fieldnames, &enum_attr); if (upb_fielddef_isseq(f)) { upb_handlers_setint32(h, f, repeated_enum, &enum_attr); @@ -12325,6 +12805,8 @@ upb_sink *upb_json_printer_input(upb_json_printer *p) { } const upb_handlers *upb_json_printer_newhandlers(const upb_msgdef *md, + bool preserve_fieldnames, const void *owner) { - return upb_handlers_newfrozen(md, owner, printer_sethandlers, NULL); + return upb_handlers_newfrozen( + md, owner, printer_sethandlers, &preserve_fieldnames); } diff --git a/ruby/ext/google/protobuf_c/upb.h b/ruby/ext/google/protobuf_c/upb.h index 0be5b296f..2faf74e50 100644 --- a/ruby/ext/google/protobuf_c/upb.h +++ b/ruby/ext/google/protobuf_c/upb.h @@ -5,6 +5,7 @@ ** ** - upb::MessageDef (upb_msgdef): describes a "message" construct. ** - upb::FieldDef (upb_fielddef): describes a message field. +** - upb::FileDef (upb_filedef): describes a .proto file and its defs. ** - upb::EnumDef (upb_enumdef): describes an enum. ** - upb::OneofDef (upb_oneofdef): describes a oneof. ** - upb::Def (upb_def): base class of all the others. @@ -79,6 +80,18 @@ #include #include +#ifdef __cplusplus +namespace upb { +class Allocator; +class Arena; +class Environment; +class ErrorSpace; +class Status; +template class InlinedArena; +template class InlinedEnvironment; +} +#endif + /* UPB_INLINE: inline if possible, emit standalone code if required. */ #ifdef __cplusplus #define UPB_INLINE inline @@ -146,6 +159,7 @@ #define UPB_ASSERT_STDLAYOUT(type) \ static_assert(std::is_standard_layout::value, \ #type " must be standard layout"); +#define UPB_FINAL final #else /* !defined(UPB_CXX11) */ #define UPB_DISALLOW_COPY_AND_ASSIGN(class_name) \ class_name(const class_name&); \ @@ -155,6 +169,7 @@ ~class_name(); \ UPB_DISALLOW_COPY_AND_ASSIGN(class_name) #define UPB_ASSERT_STDLAYOUT(type) +#define UPB_FINAL #endif /* UPB_DECLARE_TYPE() @@ -256,6 +271,7 @@ /* Generic function type. */ typedef void upb_func(); + /* C++ Casts ******************************************************************/ #ifdef __cplusplus @@ -333,129 +349,18 @@ class PointerBase2 : public PointerBase { #endif -/* upb::reffed_ptr ************************************************************/ +/* upb::ErrorSpace ************************************************************/ -#ifdef __cplusplus - -#include /* For std::swap(). */ - -namespace upb { - -/* Provides RAII semantics for upb refcounted objects. Each reffed_ptr owns a - * ref on whatever object it points to (if any). */ -template class reffed_ptr { - public: - reffed_ptr() : ptr_(NULL) {} - - /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ - template - reffed_ptr(U* val, const void* ref_donor = NULL) - : ptr_(upb::upcast(val)) { - if (ref_donor) { - assert(ptr_); - ptr_->DonateRef(ref_donor, this); - } else if (ptr_) { - ptr_->Ref(this); - } - } - - template - reffed_ptr(const reffed_ptr& other) - : ptr_(upb::upcast(other.get())) { - if (ptr_) ptr_->Ref(this); - } - - ~reffed_ptr() { if (ptr_) ptr_->Unref(this); } - - template - reffed_ptr& operator=(const reffed_ptr& other) { - reset(other.get()); - return *this; - } - - reffed_ptr& operator=(const reffed_ptr& other) { - reset(other.get()); - return *this; - } - - /* TODO(haberman): add C++11 move construction/assignment for greater - * efficiency. */ - - void swap(reffed_ptr& other) { - if (ptr_ == other.ptr_) { - return; - } - - if (ptr_) ptr_->DonateRef(this, &other); - if (other.ptr_) other.ptr_->DonateRef(&other, this); - std::swap(ptr_, other.ptr_); - } - - T& operator*() const { - assert(ptr_); - return *ptr_; - } - - T* operator->() const { - assert(ptr_); - return ptr_; - } - - T* get() const { return ptr_; } - - /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ - template - void reset(U* ptr = NULL, const void* ref_donor = NULL) { - reffed_ptr(ptr, ref_donor).swap(*this); - } - - template - reffed_ptr down_cast() { - return reffed_ptr(upb::down_cast(get())); - } - - template - reffed_ptr dyn_cast() { - return reffed_ptr(upb::dyn_cast(get())); - } - - /* Plain release() is unsafe; if we were the only owner, it would leak the - * object. Instead we provide this: */ - T* ReleaseTo(const void* new_owner) { - T* ret = NULL; - ptr_->DonateRef(this, new_owner); - std::swap(ret, ptr_); - return ret; - } - - private: - T* ptr_; -}; - -} /* namespace upb */ - -#endif /* __cplusplus */ - - -/* upb::Status ****************************************************************/ - -#ifdef __cplusplus -namespace upb { -class ErrorSpace; -class Status; -} -#endif +/* A upb::ErrorSpace represents some domain of possible error values. This lets + * upb::Status attach specific error codes to operations, like POSIX/C errno, + * Win32 error codes, etc. Clients who want to know the very specific error + * code can check the error space and then know the type of the integer code. + * + * NOTE: upb::ErrorSpace is currently not used and should be considered + * experimental. It is important primarily in cases where upb is performing + * I/O, but upb doesn't currently have any components that do this. */ UPB_DECLARE_TYPE(upb::ErrorSpace, upb_errorspace) -UPB_DECLARE_TYPE(upb::Status, upb_status) - -/* The maximum length of an error message before it will get truncated. */ -#define UPB_STATUS_MAX_MESSAGE 128 - -/* An error callback function is used to report errors from some component. - * The function can return "true" to indicate that the component should try - * to recover and proceed, but this is not always possible. */ -typedef bool upb_errcb_t(void *closure, const upb_status* status); #ifdef __cplusplus class upb::ErrorSpace { @@ -463,67 +368,21 @@ class upb::ErrorSpace { struct upb_errorspace { #endif const char *name; - /* Should the error message in the status object according to this code. */ - void (*set_message)(upb_status* status, int code); }; -#ifdef __cplusplus -/* Object representing a success or failure status. +/* upb::Status ****************************************************************/ + +/* upb::Status represents a success or failure status and error message. * It owns no resources and allocates no memory, so it should work * even in OOM situations. */ +UPB_DECLARE_TYPE(upb::Status, upb_status) -class upb::Status { - public: - Status(); +/* The maximum length of an error message before it will get truncated. */ +#define UPB_STATUS_MAX_MESSAGE 128 - /* Returns true if there is no error. */ - bool ok() const; +UPB_BEGIN_EXTERN_C - /* Optional error space and code, useful if the caller wants to - * programmatically check the specific kind of error. */ - ErrorSpace* error_space(); - int code() const; - - const char *error_message() const; - - /* The error message will be truncated if it is longer than - * UPB_STATUS_MAX_MESSAGE-4. */ - void SetErrorMessage(const char* msg); - void SetFormattedErrorMessage(const char* fmt, ...); - - /* If there is no error message already, this will use the ErrorSpace to - * populate the error message for this code. The caller can still call - * SetErrorMessage() to give a more specific message. */ - void SetErrorCode(ErrorSpace* space, int code); - - /* Resets the status to a successful state with no message. */ - void Clear(); - - void CopyFrom(const Status& other); - - private: - UPB_DISALLOW_COPY_AND_ASSIGN(Status) -#else -struct upb_status { -#endif - bool ok_; - - /* Specific status code defined by some error space (optional). */ - int code_; - upb_errorspace *error_space_; - - /* Error message; NULL-terminated. */ - char msg[UPB_STATUS_MAX_MESSAGE]; -}; - -#define UPB_STATUS_INIT {true, 0, NULL, {0}} - -#ifdef __cplusplus -extern "C" { -#endif - -/* The returned string is invalidated by any other call into the status. */ const char *upb_status_errmsg(const upb_status *status); bool upb_ok(const upb_status *status); upb_errorspace *upb_status_errspace(const upb_status *status); @@ -536,40 +395,384 @@ void upb_status_clear(upb_status *status); void upb_status_seterrmsg(upb_status *status, const char *msg); void upb_status_seterrf(upb_status *status, const char *fmt, ...); void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args); -void upb_status_seterrcode(upb_status *status, upb_errorspace *space, int code); void upb_status_copy(upb_status *to, const upb_status *from); +UPB_END_EXTERN_C + #ifdef __cplusplus -} /* extern "C" */ -namespace upb { +class upb::Status { + public: + Status() { upb_status_clear(this); } -/* C++ Wrappers */ -inline Status::Status() { Clear(); } -inline bool Status::ok() const { return upb_ok(this); } -inline const char* Status::error_message() const { - return upb_status_errmsg(this); -} -inline void Status::SetErrorMessage(const char* msg) { - upb_status_seterrmsg(this, msg); -} -inline void Status::SetFormattedErrorMessage(const char* fmt, ...) { - va_list args; - va_start(args, fmt); - upb_status_vseterrf(this, fmt, args); - va_end(args); -} -inline void Status::SetErrorCode(ErrorSpace* space, int code) { - upb_status_seterrcode(this, space, code); -} -inline void Status::Clear() { upb_status_clear(this); } -inline void Status::CopyFrom(const Status& other) { - upb_status_copy(this, &other); -} + /* Returns true if there is no error. */ + bool ok() const { return upb_ok(this); } -} /* namespace upb */ + /* Optional error space and code, useful if the caller wants to + * programmatically check the specific kind of error. */ + ErrorSpace* error_space() { return upb_status_errspace(this); } + int error_code() const { return upb_status_errcode(this); } + /* The returned string is invalidated by any other call into the status. */ + const char *error_message() const { return upb_status_errmsg(this); } + + /* The error message will be truncated if it is longer than + * UPB_STATUS_MAX_MESSAGE-4. */ + void SetErrorMessage(const char* msg) { upb_status_seterrmsg(this, msg); } + void SetFormattedErrorMessage(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + upb_status_vseterrf(this, fmt, args); + va_end(args); + } + + /* Resets the status to a successful state with no message. */ + void Clear() { upb_status_clear(this); } + + void CopyFrom(const Status& other) { upb_status_copy(this, &other); } + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(Status) +#else +struct upb_status { #endif + bool ok_; + + /* Specific status code defined by some error space (optional). */ + int code_; + upb_errorspace *error_space_; + + /* TODO(haberman): add file/line of error? */ + + /* Error message; NULL-terminated. */ + char msg[UPB_STATUS_MAX_MESSAGE]; +}; + +#define UPB_STATUS_INIT {true, 0, NULL, {0}} + + +/** Built-in error spaces. ****************************************************/ + +/* Errors raised by upb that we want to be able to detect programmatically. */ +typedef enum { + UPB_NOMEM /* Can't reuse ENOMEM because it is POSIX, not ISO C. */ +} upb_errcode_t; + +extern upb_errorspace upb_upberr; + +void upb_upberr_setoom(upb_status *s); + +/* Since errno is defined by standard C, we define an error space for it in + * core upb. Other error spaces should be defined in other, platform-specific + * modules. */ + +extern upb_errorspace upb_errnoerr; + + +/** upb::Allocator ************************************************************/ + +/* A upb::Allocator is a possibly-stateful allocator object. + * + * It could either be an arena allocator (which doesn't require individual + * free() calls) or a regular malloc() (which does). The client must therefore + * free memory unless it knows that the allocator is an arena allocator. */ +UPB_DECLARE_TYPE(upb::Allocator, upb_alloc) + +/* A malloc()/free() function. + * If "size" is 0 then the function acts like free(), otherwise it acts like + * realloc(). Only "oldsize" bytes from a previous allocation are preserved. */ +typedef void *upb_alloc_func(upb_alloc *alloc, void *ptr, size_t oldsize, + size_t size); + +#ifdef __cplusplus + +class upb::Allocator UPB_FINAL { + public: + Allocator() {} + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(Allocator) + + public: +#else +struct upb_alloc { +#endif /* __cplusplus */ + upb_alloc_func *func; +}; + +UPB_INLINE void *upb_malloc(upb_alloc *alloc, size_t size) { + assert(size > 0); + return alloc->func(alloc, NULL, 0, size); +} + +UPB_INLINE void *upb_realloc(upb_alloc *alloc, void *ptr, size_t oldsize, + size_t size) { + assert(size > 0); + return alloc->func(alloc, ptr, oldsize, size); +} + +UPB_INLINE void upb_free(upb_alloc *alloc, void *ptr) { + alloc->func(alloc, ptr, 0, 0); +} + +/* The global allocator used by upb. Uses the standard malloc()/free(). */ + +extern upb_alloc upb_alloc_global; + +/* Functions that hard-code the global malloc. + * + * We still get benefit because we can put custom logic into our global + * allocator, like injecting out-of-memory faults in debug/testing builds. */ + +UPB_INLINE void *upb_gmalloc(size_t size) { + return upb_malloc(&upb_alloc_global, size); +} + +UPB_INLINE void *upb_grealloc(void *ptr, size_t oldsize, size_t size) { + return upb_realloc(&upb_alloc_global, ptr, oldsize, size); +} + +UPB_INLINE void upb_gfree(void *ptr) { + upb_free(&upb_alloc_global, ptr); +} + +/* upb::Arena *****************************************************************/ + +/* upb::Arena is a specific allocator implementation that uses arena allocation. + * The user provides an allocator that will be used to allocate the underlying + * arena blocks. Arenas by nature do not require the individual allocations + * to be freed. However the Arena does allow users to register cleanup + * functions that will run when the arena is destroyed. + * + * A upb::Arena is *not* thread-safe. + * + * You could write a thread-safe arena allocator that satisfies the + * upb::Allocator interface, but it would not be as efficient for the + * single-threaded case. */ +UPB_DECLARE_TYPE(upb::Arena, upb_arena) + +typedef void upb_cleanup_func(void *ud); + +#define UPB_ARENA_BLOCK_OVERHEAD (sizeof(size_t)*4) + +UPB_BEGIN_EXTERN_C + +void upb_arena_init(upb_arena *a); +void upb_arena_init2(upb_arena *a, void *mem, size_t n, upb_alloc *alloc); +void upb_arena_uninit(upb_arena *a); +upb_alloc *upb_arena_alloc(upb_arena *a); +bool upb_arena_addcleanup(upb_arena *a, upb_cleanup_func *func, void *ud); +size_t upb_arena_bytesallocated(const upb_arena *a); +void upb_arena_setnextblocksize(upb_arena *a, size_t size); +void upb_arena_setmaxblocksize(upb_arena *a, size_t size); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +class upb::Arena { + public: + /* A simple arena with no initial memory block and the default allocator. */ + Arena() { upb_arena_init(this); } + + /* Constructs an arena with the given initial block which allocates blocks + * with the given allocator. The given allocator must outlive the Arena. + * + * If you pass NULL for the allocator it will default to the global allocator + * upb_alloc_global, and NULL/0 for the initial block will cause there to be + * no initial block. */ + Arena(void *mem, size_t len, Allocator* a) { + upb_arena_init2(this, mem, len, a); + } + + ~Arena() { upb_arena_uninit(this); } + + /* Sets the size of the next block the Arena will request (unless the + * requested allocation is larger). Each block will double in size until the + * max limit is reached. */ + void SetNextBlockSize(size_t size) { upb_arena_setnextblocksize(this, size); } + + /* Sets the maximum block size. No blocks larger than this will be requested + * from the underlying allocator unless individual arena allocations are + * larger. */ + void SetMaxBlockSize(size_t size) { upb_arena_setmaxblocksize(this, size); } + + /* Allows this arena to be used as a generic allocator. + * + * The arena does not need free() calls so when using Arena as an allocator + * it is safe to skip them. However they are no-ops so there is no harm in + * calling free() either. */ + Allocator* allocator() { return upb_arena_alloc(this); } + + /* Add a cleanup function to run when the arena is destroyed. + * Returns false on out-of-memory. */ + bool AddCleanup(upb_cleanup_func* func, void* ud) { + return upb_arena_addcleanup(this, func, ud); + } + + /* Total number of bytes that have been allocated. It is undefined what + * Realloc() does to this counter. */ + size_t BytesAllocated() const { + return upb_arena_bytesallocated(this); + } + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(Arena) + +#else +struct upb_arena { +#endif /* __cplusplus */ + /* We implement the allocator interface. + * This must be the first member of upb_arena! */ + upb_alloc alloc; + + /* Allocator to allocate arena blocks. We are responsible for freeing these + * when we are destroyed. */ + upb_alloc *block_alloc; + + size_t bytes_allocated; + size_t next_block_size; + size_t max_block_size; + + /* Linked list of blocks. Points to an arena_block, defined in env.c */ + void *block_head; + + /* Cleanup entries. Pointer to a cleanup_ent, defined in env.c */ + void *cleanup_head; + + /* For future expansion, since the size of this struct is exposed to users. */ + void *future1; + void *future2; +}; + + +/* upb::Environment ***********************************************************/ + +/* A upb::Environment provides a means for injecting malloc and an + * error-reporting callback into encoders/decoders. This allows them to be + * independent of nearly all assumptions about their actual environment. + * + * It is also a container for allocating the encoders/decoders themselves that + * insulates clients from knowing their actual size. This provides ABI + * compatibility even if the size of the objects change. And this allows the + * structure definitions to be in the .c files instead of the .h files, making + * the .h files smaller and more readable. + * + * We might want to consider renaming this to "Pipeline" if/when the concept of + * a pipeline element becomes more formalized. */ +UPB_DECLARE_TYPE(upb::Environment, upb_env) + +/* A function that receives an error report from an encoder or decoder. The + * callback can return true to request that the error should be recovered, but + * if the error is not recoverable this has no effect. */ +typedef bool upb_error_func(void *ud, const upb_status *status); + +UPB_BEGIN_EXTERN_C + +void upb_env_init(upb_env *e); +void upb_env_init2(upb_env *e, void *mem, size_t n, upb_alloc *alloc); +void upb_env_uninit(upb_env *e); + +void upb_env_initonly(upb_env *e); + +upb_arena *upb_env_arena(upb_env *e); +bool upb_env_ok(const upb_env *e); +void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud); + +/* Convenience wrappers around the methods of the contained arena. */ +void upb_env_reporterrorsto(upb_env *e, upb_status *s); +bool upb_env_reporterror(upb_env *e, const upb_status *s); +void *upb_env_malloc(upb_env *e, size_t size); +void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size); +void upb_env_free(upb_env *e, void *ptr); +bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud); +size_t upb_env_bytesallocated(const upb_env *e); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +class upb::Environment { + public: + /* The given Arena must outlive this environment. */ + Environment() { upb_env_initonly(this); } + + Environment(void *mem, size_t len, Allocator *a) : arena_(mem, len, a) { + upb_env_initonly(this); + } + + Arena* arena() { return upb_env_arena(this); } + + /* Set a custom error reporting function. */ + void SetErrorFunction(upb_error_func* func, void* ud) { + upb_env_seterrorfunc(this, func, ud); + } + + /* Set the error reporting function to simply copy the status to the given + * status and abort. */ + void ReportErrorsTo(Status* status) { upb_env_reporterrorsto(this, status); } + + /* Returns true if all allocations and AddCleanup() calls have succeeded, + * and no errors were reported with ReportError() (except ones that recovered + * successfully). */ + bool ok() const { return upb_env_ok(this); } + + /* Reports an error to this environment's callback, returning true if + * the caller should try to recover. */ + bool ReportError(const Status* status) { + return upb_env_reporterror(this, status); + } + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(Environment) + +#else +struct upb_env { +#endif /* __cplusplus */ + upb_arena arena_; + upb_error_func *error_func_; + void *error_ud_; + bool ok_; +}; + + +/* upb::InlinedArena **********************************************************/ +/* upb::InlinedEnvironment ****************************************************/ + +/* upb::InlinedArena and upb::InlinedEnvironment seed their arenas with a + * predefined amount of memory. No heap memory will be allocated until the + * initial block is exceeded. + * + * These types only exist in C++ */ + +#ifdef __cplusplus + +template class upb::InlinedArena : public upb::Arena { + public: + InlinedArena() : Arena(initial_block_, N, NULL) {} + explicit InlinedArena(Allocator* a) : Arena(initial_block_, N, a) {} + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(InlinedArena) + + char initial_block_[N + UPB_ARENA_BLOCK_OVERHEAD]; +}; + +template class upb::InlinedEnvironment : public upb::Environment { + public: + InlinedEnvironment() : Environment(initial_block_, N, NULL) {} + explicit InlinedEnvironment(Allocator *a) + : Environment(initial_block_, N, a) {} + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(InlinedEnvironment) + + char initial_block_[N + UPB_ARENA_BLOCK_OVERHEAD]; +}; + +#endif /* __cplusplus */ + + #endif /* UPB_H_ */ @@ -611,10 +814,14 @@ typedef struct { #endif /* Like strdup(), which isn't always available since it's not ANSI C. */ -char *upb_strdup(const char *s); +char *upb_strdup(const char *s, upb_alloc *a); /* Variant that works with a length-delimited rather than NULL-delimited string, * as supported by strtable. */ -char *upb_strdup2(const char *s, size_t len); +char *upb_strdup2(const char *s, size_t len, upb_alloc *a); + +UPB_INLINE char *upb_gstrdup(const char *s) { + return upb_strdup(s, &upb_alloc_global); +} UPB_INLINE void _upb_value_setval(upb_value *v, uint64_t val, upb_ctype_t ctype) { @@ -791,14 +998,40 @@ typedef struct { * initialize const hash tables. Then we cast away const when we have to. */ const upb_tabent *entries; + +#ifndef NDEBUG + /* This table's allocator. We make the user pass it in to every relevant + * function and only use this to check it in debug mode. We do this solely + * to keep upb_table as small as possible. This might seem slightly paranoid + * but the plan is to use upb_table for all map fields and extension sets in + * a forthcoming message representation, so there could be a lot of these. + * If this turns out to be too annoying later, we can change it (since this + * is an internal-only header file). */ + upb_alloc *alloc; +#endif } upb_table; +#ifdef NDEBUG +# define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \ + {count, mask, ctype, size_lg2, entries} +#else +# ifdef UPB_DEBUG_REFS +/* At the moment the only mutable tables we statically initialize are debug + * ref tables. */ +# define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \ + {count, mask, ctype, size_lg2, entries, &upb_alloc_debugrefs} +# else +# define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \ + {count, mask, ctype, size_lg2, entries, NULL} +# endif +#endif + typedef struct { upb_table t; } upb_strtable; #define UPB_STRTABLE_INIT(count, mask, ctype, size_lg2, entries) \ - {{count, mask, ctype, size_lg2, entries}} + {UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries)} #define UPB_EMPTY_STRTABLE_INIT(ctype) \ UPB_STRTABLE_INIT(0, 0, ctype, 0, NULL) @@ -811,7 +1044,7 @@ typedef struct { } upb_inttable; #define UPB_INTTABLE_INIT(count, mask, ctype, size_lg2, ent, a, asize, acount) \ - {{count, mask, ctype, size_lg2, ent}, a, asize, acount} + {UPB_TABLE_INIT(count, mask, ctype, size_lg2, ent), a, asize, acount} #define UPB_EMPTY_INTTABLE_INIT(ctype) \ UPB_INTTABLE_INIT(0, 0, ctype, 0, NULL, NULL, 0, 0) @@ -851,10 +1084,26 @@ UPB_INLINE bool upb_arrhas(upb_tabval key) { /* Initialize and uninitialize a table, respectively. If memory allocation * failed, false is returned that the table is uninitialized. */ -bool upb_inttable_init(upb_inttable *table, upb_ctype_t ctype); -bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype); -void upb_inttable_uninit(upb_inttable *table); -void upb_strtable_uninit(upb_strtable *table); +bool upb_inttable_init2(upb_inttable *table, upb_ctype_t ctype, upb_alloc *a); +bool upb_strtable_init2(upb_strtable *table, upb_ctype_t ctype, upb_alloc *a); +void upb_inttable_uninit2(upb_inttable *table, upb_alloc *a); +void upb_strtable_uninit2(upb_strtable *table, upb_alloc *a); + +UPB_INLINE bool upb_inttable_init(upb_inttable *table, upb_ctype_t ctype) { + return upb_inttable_init2(table, ctype, &upb_alloc_global); +} + +UPB_INLINE bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype) { + return upb_strtable_init2(table, ctype, &upb_alloc_global); +} + +UPB_INLINE void upb_inttable_uninit(upb_inttable *table) { + upb_inttable_uninit2(table, &upb_alloc_global); +} + +UPB_INLINE void upb_strtable_uninit(upb_strtable *table) { + upb_strtable_uninit2(table, &upb_alloc_global); +} /* Returns the number of values in the table. */ size_t upb_inttable_count(const upb_inttable *t); @@ -869,9 +1118,20 @@ UPB_INLINE size_t upb_strtable_count(const upb_strtable *t) { * * If a table resize was required but memory allocation failed, false is * returned and the table is unchanged. */ -bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val); -bool upb_strtable_insert2(upb_strtable *t, const char *key, size_t len, - upb_value val); +bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val, + upb_alloc *a); +bool upb_strtable_insert3(upb_strtable *t, const char *key, size_t len, + upb_value val, upb_alloc *a); + +UPB_INLINE bool upb_inttable_insert(upb_inttable *t, uintptr_t key, + upb_value val) { + return upb_inttable_insert2(t, key, val, &upb_alloc_global); +} + +UPB_INLINE bool upb_strtable_insert2(upb_strtable *t, const char *key, + size_t len, upb_value val) { + return upb_strtable_insert3(t, key, len, val, &upb_alloc_global); +} /* For NULL-terminated strings. */ UPB_INLINE bool upb_strtable_insert(upb_strtable *t, const char *key, @@ -894,8 +1154,13 @@ UPB_INLINE bool upb_strtable_lookup(const upb_strtable *t, const char *key, /* Removes an item from the table. Returns true if the remove was successful, * and stores the removed item in *val if non-NULL. */ bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val); -bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len, - upb_value *val); +bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len, + upb_value *val, upb_alloc *alloc); + +UPB_INLINE bool upb_strtable_remove2(upb_strtable *t, const char *key, + size_t len, upb_value *val) { + return upb_strtable_remove3(t, key, len, val, &upb_alloc_global); +} /* For NULL-terminated strings. */ UPB_INLINE bool upb_strtable_remove(upb_strtable *t, const char *key, @@ -910,19 +1175,33 @@ bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val); /* Handy routines for treating an inttable like a stack. May not be mixed with * other insert/remove calls. */ -bool upb_inttable_push(upb_inttable *t, upb_value val); +bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a); upb_value upb_inttable_pop(upb_inttable *t); +UPB_INLINE bool upb_inttable_push(upb_inttable *t, upb_value val) { + return upb_inttable_push2(t, val, &upb_alloc_global); +} + /* Convenience routines for inttables with pointer keys. */ -bool upb_inttable_insertptr(upb_inttable *t, const void *key, upb_value val); +bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val, + upb_alloc *a); bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val); bool upb_inttable_lookupptr( const upb_inttable *t, const void *key, upb_value *val); +UPB_INLINE bool upb_inttable_insertptr(upb_inttable *t, const void *key, + upb_value val) { + return upb_inttable_insertptr2(t, key, val, &upb_alloc_global); +} + /* Optimizes the table for the current set of entries, for both memory use and * lookup time. Client should call this after all entries have been inserted; * inserting more entries is legal, but will likely require a table resize. */ -void upb_inttable_compact(upb_inttable *t); +void upb_inttable_compact2(upb_inttable *t, upb_alloc *a); + +UPB_INLINE void upb_inttable_compact(upb_inttable *t) { + upb_inttable_compact2(t, &upb_alloc_global); +} /* A special-case inlinable version of the lookup routine for 32-bit * integers. */ @@ -951,7 +1230,7 @@ UPB_INLINE bool upb_inttable_lookup32(const upb_inttable *t, uint32_t key, } /* Exposed for testing only. */ -bool upb_strtable_resize(upb_strtable *t, size_t size_lg2); +bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a); /* Iterators ******************************************************************/ @@ -996,8 +1275,8 @@ typedef struct { void upb_strtable_begin(upb_strtable_iter *i, const upb_strtable *t); void upb_strtable_next(upb_strtable_iter *i); bool upb_strtable_done(const upb_strtable_iter *i); -const char *upb_strtable_iter_key(upb_strtable_iter *i); -size_t upb_strtable_iter_keylength(upb_strtable_iter *i); +const char *upb_strtable_iter_key(const upb_strtable_iter *i); +size_t upb_strtable_iter_keylength(const upb_strtable_iter *i); upb_value upb_strtable_iter_value(const upb_strtable_iter *i); void upb_strtable_iter_setdone(upb_strtable_iter *i); bool upb_strtable_iter_isequal(const upb_strtable_iter *i1, @@ -1050,7 +1329,10 @@ bool upb_inttable_iter_isequal(const upb_inttable_iter *i1, /* #define UPB_DEBUG_REFS */ #ifdef __cplusplus -namespace upb { class RefCounted; } +namespace upb { +class RefCounted; +template class reffed_ptr; +} #endif UPB_DECLARE_TYPE(upb::RefCounted, upb_refcounted) @@ -1118,10 +1400,12 @@ struct upb_refcounted { }; #ifdef UPB_DEBUG_REFS -#define UPB_REFCOUNT_INIT(refs, ref2s) \ - {&static_refcount, NULL, NULL, 0, true, refs, ref2s} +extern upb_alloc upb_alloc_debugrefs; +#define UPB_REFCOUNT_INIT(vtbl, refs, ref2s) \ + {&static_refcount, NULL, vtbl, 0, true, refs, ref2s} #else -#define UPB_REFCOUNT_INIT(refs, ref2s) {&static_refcount, NULL, NULL, 0, true} +#define UPB_REFCOUNT_INIT(vtbl, refs, ref2s) \ + {&static_refcount, NULL, vtbl, 0, true} #endif UPB_BEGIN_EXTERN_C @@ -1254,6 +1538,111 @@ inline void RefCounted::CheckRef(const void *owner) const { } /* namespace upb */ #endif + +/* upb::reffed_ptr ************************************************************/ + +#ifdef __cplusplus + +#include /* For std::swap(). */ + +/* Provides RAII semantics for upb refcounted objects. Each reffed_ptr owns a + * ref on whatever object it points to (if any). */ +template class upb::reffed_ptr { + public: + reffed_ptr() : ptr_(NULL) {} + + /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ + template + reffed_ptr(U* val, const void* ref_donor = NULL) + : ptr_(upb::upcast(val)) { + if (ref_donor) { + assert(ptr_); + ptr_->DonateRef(ref_donor, this); + } else if (ptr_) { + ptr_->Ref(this); + } + } + + template + reffed_ptr(const reffed_ptr& other) + : ptr_(upb::upcast(other.get())) { + if (ptr_) ptr_->Ref(this); + } + + reffed_ptr(const reffed_ptr& other) + : ptr_(upb::upcast(other.get())) { + if (ptr_) ptr_->Ref(this); + } + + ~reffed_ptr() { if (ptr_) ptr_->Unref(this); } + + template + reffed_ptr& operator=(const reffed_ptr& other) { + reset(other.get()); + return *this; + } + + reffed_ptr& operator=(const reffed_ptr& other) { + reset(other.get()); + return *this; + } + + /* TODO(haberman): add C++11 move construction/assignment for greater + * efficiency. */ + + void swap(reffed_ptr& other) { + if (ptr_ == other.ptr_) { + return; + } + + if (ptr_) ptr_->DonateRef(this, &other); + if (other.ptr_) other.ptr_->DonateRef(&other, this); + std::swap(ptr_, other.ptr_); + } + + T& operator*() const { + assert(ptr_); + return *ptr_; + } + + T* operator->() const { + assert(ptr_); + return ptr_; + } + + T* get() const { return ptr_; } + + /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ + template + void reset(U* ptr = NULL, const void* ref_donor = NULL) { + reffed_ptr(ptr, ref_donor).swap(*this); + } + + template + reffed_ptr down_cast() { + return reffed_ptr(upb::down_cast(get())); + } + + template + reffed_ptr dyn_cast() { + return reffed_ptr(upb::dyn_cast(get())); + } + + /* Plain release() is unsafe; if we were the only owner, it would leak the + * object. Instead we provide this: */ + T* ReleaseTo(const void* new_owner) { + T* ret = NULL; + ptr_->DonateRef(this, new_owner); + std::swap(ret, ptr_); + return ret; + } + + private: + T* ptr_; +}; + +#endif /* __cplusplus */ + #endif /* UPB_REFCOUNT_H_ */ #ifdef __cplusplus @@ -1265,12 +1654,17 @@ namespace upb { class Def; class EnumDef; class FieldDef; +class FileDef; class MessageDef; class OneofDef; } #endif UPB_DECLARE_DERIVED_TYPE(upb::Def, upb::RefCounted, upb_def, upb_refcounted) +UPB_DECLARE_DERIVED_TYPE(upb::OneofDef, upb::RefCounted, upb_oneofdef, + upb_refcounted) +UPB_DECLARE_DERIVED_TYPE(upb::FileDef, upb::RefCounted, upb_filedef, + upb_refcounted) /* The maximum message depth that the type graph can have. This is a resource * limit for the C stack since we sometimes need to recursively traverse the @@ -1282,15 +1676,16 @@ UPB_DECLARE_DERIVED_TYPE(upb::Def, upb::RefCounted, upb_def, upb_refcounted) #define UPB_MAX_MESSAGE_DEPTH 64 -/* upb::Def: base class for defs *********************************************/ +/* upb::Def: base class for top-level defs ***********************************/ -/* All the different kind of defs we support. These correspond 1:1 with - * declarations in a .proto file. */ +/* All the different kind of defs that can be defined at the top-level and put + * in a SymbolTable or appear in a FileDef::defs() list. This excludes some + * defs (like oneofs and files). It only includes fields because they can be + * defined as extensions. */ typedef enum { UPB_DEF_MSG, UPB_DEF_FIELD, UPB_DEF_ENUM, - UPB_DEF_ONEOF, UPB_DEF_SERVICE, /* Not yet implemented. */ UPB_DEF_ANY = -1 /* Wildcard for upb_symtab_get*() */ } upb_deftype_t; @@ -1313,6 +1708,9 @@ class upb::Def { /* "fullname" is the def's fully-qualified name (eg. foo.bar.Message). */ const char *full_name() const; + /* The final part of a def's name (eg. Message). */ + const char *name() const; + /* The def must be mutable. Caller retains ownership of fullname. Defs are * not required to have a name; if a def has no name when it is frozen, it * will remain an anonymous def. On failure, returns false and details in "s" @@ -1320,6 +1718,11 @@ class upb::Def { bool set_full_name(const char* fullname, upb::Status* s); bool set_full_name(const std::string &fullname, upb::Status* s); + /* The file in which this def appears. It is not necessary to add a def to a + * file (and consequently the accessor may return NULL). Set this by calling + * file->Add(def). */ + FileDef* file() const; + /* Freezes the given defs; this validates all constraints and marks the defs * as frozen (read-only). "defs" may not contain any fielddefs, but fields * of any msgdefs will be frozen. @@ -1331,7 +1734,7 @@ class upb::Def { * * After this operation succeeds, the finalized defs must only be accessed * through a const pointer! */ - static bool Freeze(Def* const* defs, int n, Status* status); + static bool Freeze(Def* const* defs, size_t n, Status* status); static bool Freeze(const std::vector& defs, Status* status); private: @@ -1350,8 +1753,13 @@ UPB_REFCOUNTED_CMETHODS(upb_def, upb_def_upcast) upb_deftype_t upb_def_type(const upb_def *d); const char *upb_def_fullname(const upb_def *d); +const char *upb_def_name(const upb_def *d); +const upb_filedef *upb_def_file(const upb_def *d); bool upb_def_setfullname(upb_def *def, const char *fullname, upb_status *s); -bool upb_def_freeze(upb_def *const *defs, int n, upb_status *s); +bool upb_def_freeze(upb_def *const *defs, size_t n, upb_status *s); + +/* Temporary API: for internal use only. */ +bool _upb_def_validate(upb_def *const*defs, size_t n, upb_status *s); UPB_END_EXTERN_C @@ -1424,7 +1832,6 @@ UPB_END_EXTERN_C UPB_DECLARE_DEF_TYPE(upb::FieldDef, fielddef, FIELD) UPB_DECLARE_DEF_TYPE(upb::MessageDef, msgdef, MSG) UPB_DECLARE_DEF_TYPE(upb::EnumDef, enumdef, ENUM) -UPB_DECLARE_DEF_TYPE(upb::OneofDef, oneofdef, ONEOF) #undef UPB_DECLARE_DEF_TYPE #undef UPB_DEF_CASTS @@ -1487,6 +1894,11 @@ typedef enum { UPB_DESCRIPTOR_TYPE_SINT64 = 18 } upb_descriptortype_t; +typedef enum { + UPB_SYNTAX_PROTO2 = 2, + UPB_SYNTAX_PROTO3 = 3 +} upb_syntax_t; + /* Maximum field number allowed for FieldDefs. This is an inherent limit of the * protobuf wire format. */ #define UPB_MAX_FIELDNUMBER ((1 << 29) - 1) @@ -1582,7 +1994,7 @@ class upb::FieldDef { * whatever message this field belongs to. Guaranteed to be less than * f->containing_type()->field_count(). May only be accessed once the def has * been finalized. */ - int index() const; + uint32_t index() const; /* The MessageDef to which this field belongs. * @@ -1878,6 +2290,10 @@ UPB_END_EXTERN_C typedef upb_inttable_iter upb_msg_field_iter; typedef upb_strtable_iter upb_msg_oneof_iter; +/* Well-known field tag numbers for map-entry messages. */ +#define UPB_MAPENTRY_KEY 1 +#define UPB_MAPENTRY_VALUE 2 + #ifdef __cplusplus /* Structure that describes a single .proto message type. @@ -1893,6 +2309,7 @@ class upb::MessageDef { /* Functionality from upb::Def. */ const char* full_name() const; + const char* name() const; bool set_full_name(const char* fullname, Status* s); bool set_full_name(const std::string& fullname, Status* s); @@ -1935,6 +2352,11 @@ class upb::MessageDef { bool AddOneof(OneofDef* o, Status* s); bool AddOneof(const reffed_ptr& o, Status* s); + upb_syntax_t syntax() const; + + /* Returns false if we don't support this syntax value. */ + bool set_syntax(upb_syntax_t syntax); + /* Set this to false to indicate that primitive fields should not have * explicit presence information associated with them. This will affect all * fields added to this message. Defaults to true. */ @@ -2125,15 +2547,20 @@ UPB_REFCOUNTED_CMETHODS(upb_msgdef, upb_msgdef_upcast2) bool upb_msgdef_freeze(upb_msgdef *m, upb_status *status); -const char *upb_msgdef_fullname(const upb_msgdef *m); -bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, upb_status *s); - upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner); +const char *upb_msgdef_fullname(const upb_msgdef *m); +const char *upb_msgdef_name(const upb_msgdef *m); +int upb_msgdef_numoneofs(const upb_msgdef *m); +upb_syntax_t upb_msgdef_syntax(const upb_msgdef *m); + bool upb_msgdef_addfield(upb_msgdef *m, upb_fielddef *f, const void *ref_donor, upb_status *s); bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor, upb_status *s); -void upb_msgdef_setprimitiveshavepresence(upb_msgdef *m, bool have_presence); +bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, upb_status *s); +void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry); +bool upb_msgdef_mapentry(const upb_msgdef *m); +bool upb_msgdef_setsyntax(upb_msgdef *m, upb_syntax_t syntax); /* Field lookup in a couple of different variations: * - itof = int to field @@ -2175,18 +2602,21 @@ UPB_INLINE upb_oneofdef *upb_msgdef_ntoo_mutable(upb_msgdef *m, return (upb_oneofdef *)upb_msgdef_ntoo(m, name, len); } -void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry); -bool upb_msgdef_mapentry(const upb_msgdef *m); +/* Lookup of either field or oneof by name. Returns whether either was found. + * If the return is true, then the found def will be set, and the non-found + * one set to NULL. */ +bool upb_msgdef_lookupname(const upb_msgdef *m, const char *name, size_t len, + const upb_fielddef **f, const upb_oneofdef **o); -/* Well-known field tag numbers for map-entry messages. */ -#define UPB_MAPENTRY_KEY 1 -#define UPB_MAPENTRY_VALUE 2 +UPB_INLINE bool upb_msgdef_lookupnamez(const upb_msgdef *m, const char *name, + const upb_fielddef **f, + const upb_oneofdef **o) { + return upb_msgdef_lookupname(m, name, strlen(name), f, o); +} -const upb_oneofdef *upb_msgdef_findoneof(const upb_msgdef *m, - const char *name); -int upb_msgdef_numoneofs(const upb_msgdef *m); - -/* upb_msg_field_iter i; +/* Iteration over fields and oneofs. For example: + * + * upb_msg_field_iter i; * for(upb_msg_field_begin(&i, m); * !upb_msg_field_done(&i); * upb_msg_field_next(&i)) { @@ -2232,6 +2662,7 @@ class upb::EnumDef { /* Functionality from upb::Def. */ const char* full_name() const; + const char* name() const; bool set_full_name(const char* fullname, Status* s); bool set_full_name(const std::string& fullname, Status* s); @@ -2306,6 +2737,7 @@ bool upb_enumdef_freeze(upb_enumdef *e, upb_status *status); /* From upb_def. */ const char *upb_enumdef_fullname(const upb_enumdef *e); +const char *upb_enumdef_name(const upb_enumdef *e); bool upb_enumdef_setfullname(upb_enumdef *e, const char *fullname, upb_status *s); @@ -2347,8 +2779,7 @@ typedef upb_inttable_iter upb_oneof_iter; #ifdef __cplusplus -/* Class that represents a oneof. Its base class is upb::Def (convert with - * upb::upcast()). */ +/* Class that represents a oneof. */ class upb::OneofDef { public: /* Returns NULL if memory allocation failed. */ @@ -2357,9 +2788,6 @@ class upb::OneofDef { /* upb::RefCounted methods like Ref()/Unref(). */ UPB_REFCOUNTED_CPPMETHODS - /* Functionality from upb::Def. */ - const char* full_name() const; - /* Returns the MessageDef that owns this OneofDef. */ const MessageDef* containing_type() const; @@ -2367,6 +2795,7 @@ class upb::OneofDef { * by name once added to a message def. */ const char* name() const; bool set_name(const char* name, Status* s); + bool set_name(const std::string& name, Status* s); /* Returns the number of fields currently defined in the oneof. */ int field_count() const; @@ -2460,7 +2889,7 @@ upb_oneofdef *upb_oneofdef_new(const void *owner); upb_oneofdef *upb_oneofdef_dup(const upb_oneofdef *o, const void *owner); /* Include upb_refcounted methods like upb_oneofdef_ref(). */ -UPB_REFCOUNTED_CMETHODS(upb_oneofdef, upb_oneofdef_upcast2) +UPB_REFCOUNTED_CMETHODS(upb_oneofdef, upb_oneofdef_upcast) const char *upb_oneofdef_name(const upb_oneofdef *o); bool upb_oneofdef_setname(upb_oneofdef *o, const char *name, upb_status *s); @@ -2496,6 +2925,120 @@ void upb_oneof_iter_setdone(upb_oneof_iter *iter); UPB_END_EXTERN_C + +/* upb::FileDef ***************************************************************/ + +#ifdef __cplusplus + +/* Class that represents a .proto file with some things defined in it. + * + * Many users won't care about FileDefs, but they are necessary if you want to + * read the values of file-level options. */ +class upb::FileDef { + public: + /* Returns NULL if memory allocation failed. */ + static reffed_ptr New(); + + /* upb::RefCounted methods like Ref()/Unref(). */ + UPB_REFCOUNTED_CPPMETHODS + + /* Get/set name of the file (eg. "foo/bar.proto"). */ + const char* name() const; + bool set_name(const char* name, Status* s); + bool set_name(const std::string& name, Status* s); + + /* Package name for definitions inside the file (eg. "foo.bar"). */ + const char* package() const; + bool set_package(const char* package, Status* s); + + /* Syntax for the file. Defaults to proto2. */ + upb_syntax_t syntax() const; + void set_syntax(upb_syntax_t syntax); + + /* Get the list of defs from the file. These are returned in the order that + * they were added to the FileDef. */ + int def_count() const; + const Def* def(int index) const; + Def* def(int index); + + /* Get the list of dependencies from the file. These are returned in the + * order that they were added to the FileDef. */ + int dependency_count() const; + const FileDef* dependency(int index) const; + + /* Adds defs to this file. The def must not already belong to another + * file. + * + * Note: this does *not* ensure that this def's name is unique in this file! + * Use a SymbolTable if you want to check this property. Especially since + * properly checking uniqueness would require a check across *all* files + * (including dependencies). */ + bool AddDef(Def* def, Status* s); + bool AddMessage(MessageDef* m, Status* s); + bool AddEnum(EnumDef* e, Status* s); + bool AddExtension(FieldDef* f, Status* s); + + /* Adds a dependency of this file. */ + bool AddDependency(const FileDef* file); + + /* Freezes this FileDef and all messages/enums under it. All subdefs must be + * resolved and all messages/enums must validate. Returns true if this + * succeeded. + * + * TODO(haberman): should we care whether the file's dependencies are frozen + * already? */ + bool Freeze(Status* s); + + private: + UPB_DISALLOW_POD_OPS(FileDef, upb::FileDef) +}; + +#endif + +UPB_BEGIN_EXTERN_C + +upb_filedef *upb_filedef_new(const void *owner); + +/* Include upb_refcounted methods like upb_msgdef_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_filedef, upb_filedef_upcast) + +const char *upb_filedef_name(const upb_filedef *f); +const char *upb_filedef_package(const upb_filedef *f); +upb_syntax_t upb_filedef_syntax(const upb_filedef *f); +size_t upb_filedef_defcount(const upb_filedef *f); +size_t upb_filedef_depcount(const upb_filedef *f); +const upb_def *upb_filedef_def(const upb_filedef *f, size_t i); +const upb_filedef *upb_filedef_dep(const upb_filedef *f, size_t i); + +bool upb_filedef_freeze(upb_filedef *f, upb_status *s); +bool upb_filedef_setname(upb_filedef *f, const char *name, upb_status *s); +bool upb_filedef_setpackage(upb_filedef *f, const char *package, upb_status *s); +bool upb_filedef_setsyntax(upb_filedef *f, upb_syntax_t syntax, upb_status *s); + +bool upb_filedef_adddef(upb_filedef *f, upb_def *def, const void *ref_donor, + upb_status *s); +bool upb_filedef_adddep(upb_filedef *f, const upb_filedef *dep); + +UPB_INLINE bool upb_filedef_addmsg(upb_filedef *f, upb_msgdef *m, + const void *ref_donor, upb_status *s) { + return upb_filedef_adddef(f, upb_msgdef_upcast_mutable(m), ref_donor, s); +} + +UPB_INLINE bool upb_filedef_addenum(upb_filedef *f, upb_enumdef *e, + const void *ref_donor, upb_status *s) { + return upb_filedef_adddef(f, upb_enumdef_upcast_mutable(e), ref_donor, s); +} + +UPB_INLINE bool upb_filedef_addext(upb_filedef *file, upb_fielddef *f, + const void *ref_donor, upb_status *s) { + return upb_filedef_adddef(file, upb_fielddef_upcast_mutable(f), ref_donor, s); +} +UPB_INLINE upb_def *upb_filedef_mutabledef(upb_filedef *f, int i) { + return (upb_def*)upb_filedef_def(f, i); +} + +UPB_END_EXTERN_C + #ifdef __cplusplus UPB_INLINE const char* upb_safecstr(const std::string& str) { @@ -2511,13 +3054,14 @@ inline Def* Def::Dup(const void* owner) const { } inline Def::Type Def::def_type() const { return upb_def_type(this); } inline const char* Def::full_name() const { return upb_def_fullname(this); } +inline const char* Def::name() const { return upb_def_name(this); } inline bool Def::set_full_name(const char* fullname, Status* s) { return upb_def_setfullname(this, fullname, s); } inline bool Def::set_full_name(const std::string& fullname, Status* s) { return upb_def_setfullname(this, upb_safecstr(fullname), s); } -inline bool Def::Freeze(Def* const* defs, int n, Status* status) { +inline bool Def::Freeze(Def* const* defs, size_t n, Status* status) { return upb_def_freeze(defs, n, status); } inline bool Def::Freeze(const std::vector& defs, Status* status) { @@ -2596,6 +3140,9 @@ inline void FieldDef::set_lazy(bool lazy) { inline bool FieldDef::packed() const { return upb_fielddef_packed(this); } +inline uint32_t FieldDef::index() const { + return upb_fielddef_index(this); +} inline void FieldDef::set_packed(bool packed) { upb_fielddef_setpacked(this, packed); } @@ -2740,12 +3287,21 @@ inline reffed_ptr MessageDef::New() { inline const char *MessageDef::full_name() const { return upb_msgdef_fullname(this); } +inline const char *MessageDef::name() const { + return upb_msgdef_name(this); +} +inline upb_syntax_t MessageDef::syntax() const { + return upb_msgdef_syntax(this); +} inline bool MessageDef::set_full_name(const char* fullname, Status* s) { return upb_msgdef_setfullname(this, fullname, s); } inline bool MessageDef::set_full_name(const std::string& fullname, Status* s) { return upb_msgdef_setfullname(this, upb_safecstr(fullname), s); } +inline bool MessageDef::set_syntax(upb_syntax_t syntax) { + return upb_msgdef_setsyntax(this, syntax); +} inline bool MessageDef::Freeze(Status* status) { return upb_msgdef_freeze(this, status); } @@ -2927,6 +3483,9 @@ inline reffed_ptr EnumDef::New() { inline const char* EnumDef::full_name() const { return upb_enumdef_fullname(this); } +inline const char* EnumDef::name() const { + return upb_enumdef_name(this); +} inline bool EnumDef::set_full_name(const char* fullname, Status* s) { return upb_enumdef_setfullname(this, fullname, s); } @@ -2976,9 +3535,6 @@ inline reffed_ptr OneofDef::New() { upb_oneofdef *o = upb_oneofdef_new(&o); return reffed_ptr(o, &o); } -inline const char* OneofDef::full_name() const { - return upb_oneofdef_name(this); -} inline const MessageDef* OneofDef::containing_type() const { return upb_oneofdef_containingtype(this); @@ -2989,6 +3545,9 @@ inline const char* OneofDef::name() const { inline bool OneofDef::set_name(const char* name, Status* s) { return upb_oneofdef_setname(this, name, s); } +inline bool OneofDef::set_name(const std::string& name, Status* s) { + return upb_oneofdef_setname(this, upb_safecstr(name), s); +} inline int OneofDef::field_count() const { return upb_oneofdef_numfields(this); } @@ -3057,6 +3616,57 @@ inline bool OneofDef::const_iterator::operator!=( return !(*this == other); } +inline reffed_ptr FileDef::New() { + upb_filedef *f = upb_filedef_new(&f); + return reffed_ptr(f, &f); +} + +inline const char* FileDef::name() const { + return upb_filedef_name(this); +} +inline bool FileDef::set_name(const char* name, Status* s) { + return upb_filedef_setname(this, name, s); +} +inline bool FileDef::set_name(const std::string& name, Status* s) { + return upb_filedef_setname(this, upb_safecstr(name), s); +} +inline const char* FileDef::package() const { + return upb_filedef_package(this); +} +inline bool FileDef::set_package(const char* package, Status* s) { + return upb_filedef_setpackage(this, package, s); +} +inline int FileDef::def_count() const { + return upb_filedef_defcount(this); +} +inline const Def* FileDef::def(int index) const { + return upb_filedef_def(this, index); +} +inline Def* FileDef::def(int index) { + return const_cast(upb_filedef_def(this, index)); +} +inline int FileDef::dependency_count() const { + return upb_filedef_depcount(this); +} +inline const FileDef* FileDef::dependency(int index) const { + return upb_filedef_dep(this, index); +} +inline bool FileDef::AddDef(Def* def, Status* s) { + return upb_filedef_adddef(this, def, NULL, s); +} +inline bool FileDef::AddMessage(MessageDef* m, Status* s) { + return upb_filedef_addmsg(this, m, NULL, s); +} +inline bool FileDef::AddEnum(EnumDef* e, Status* s) { + return upb_filedef_addenum(this, e, NULL, s); +} +inline bool FileDef::AddExtension(FieldDef* f, Status* s) { + return upb_filedef_addext(this, f, NULL, s); +} +inline bool FileDef::AddDependency(const FileDef* file) { + return upb_filedef_adddep(this, file); +} + } /* namespace upb */ #endif @@ -3095,6 +3705,7 @@ struct upb_def { upb_refcounted base; const char *fullname; + const upb_filedef* file; char type; /* A upb_deftype_t (char to save space) */ /* Used as a flag during the def's mutable stage. Must be false unless @@ -3104,8 +3715,8 @@ struct upb_def { bool came_from_user; }; -#define UPB_DEF_INIT(name, type, refs, ref2s) \ - { UPB_REFCOUNT_INIT(refs, ref2s), name, type, false } +#define UPB_DEF_INIT(name, type, vtbl, refs, ref2s) \ + { UPB_REFCOUNT_INIT(vtbl, refs, ref2s), name, NULL, type, false } /* upb_fielddef ***************************************************************/ @@ -3145,12 +3756,14 @@ struct upb_fielddef { uint32_t index_; }; +extern const struct upb_refcounted_vtbl upb_fielddef_vtbl; + #define UPB_FIELDDEF_INIT(label, type, intfmt, tagdelim, is_extension, lazy, \ packed, name, num, msgdef, subdef, selector_base, \ index, defaultval, refs, ref2s) \ { \ - UPB_DEF_INIT(name, UPB_DEF_FIELD, refs, ref2s), defaultval, {msgdef}, \ - {subdef}, NULL, false, false, \ + UPB_DEF_INIT(name, UPB_DEF_FIELD, &upb_fielddef_vtbl, refs, ref2s), \ + defaultval, {msgdef}, {subdef}, NULL, false, false, \ type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES, true, is_extension, \ lazy, packed, intfmt, tagdelim, type, label, num, selector_base, index \ } @@ -3166,32 +3779,26 @@ struct upb_msgdef { /* Tables for looking up fields by number and name. */ upb_inttable itof; /* int to field */ - upb_strtable ntof; /* name to field */ + upb_strtable ntof; /* name to field/oneof */ - /* Tables for looking up oneofs by name. */ - upb_strtable ntoo; /* name to oneof */ - - /* Is this a map-entry message? - * TODO: set this flag properly for static descriptors; regenerate - * descriptor.upb.c. */ + /* Is this a map-entry message? */ bool map_entry; - /* Do primitive values in this message have explicit presence or not? - * TODO: set this flag properly for static descriptors; regenerate - * descriptor.upb.c. */ - bool primitives_have_presence; + /* Whether this message has proto2 or proto3 semantics. */ + upb_syntax_t syntax; /* TODO(haberman): proper extension ranges (there can be multiple). */ }; +extern const struct upb_refcounted_vtbl upb_msgdef_vtbl; + /* TODO: also support static initialization of the oneofs table. This will be * needed if we compile in descriptors that contain oneofs. */ #define UPB_MSGDEF_INIT(name, selector_count, submsg_field_count, itof, ntof, \ - refs, ref2s) \ + map_entry, syntax, refs, ref2s) \ { \ - UPB_DEF_INIT(name, UPB_DEF_MSG, refs, ref2s), selector_count, \ - submsg_field_count, itof, ntof, \ - UPB_EMPTY_STRTABLE_INIT(UPB_CTYPE_PTR), false, true \ + UPB_DEF_INIT(name, UPB_DEF_MSG, &upb_fielddef_vtbl, refs, ref2s), \ + selector_count, submsg_field_count, itof, ntof, map_entry, syntax \ } @@ -3205,22 +3812,28 @@ struct upb_enumdef { int32_t defaultval; }; +extern const struct upb_refcounted_vtbl upb_enumdef_vtbl; + #define UPB_ENUMDEF_INIT(name, ntoi, iton, defaultval, refs, ref2s) \ - { UPB_DEF_INIT(name, UPB_DEF_ENUM, refs, ref2s), ntoi, iton, defaultval } + { UPB_DEF_INIT(name, UPB_DEF_ENUM, &upb_enumdef_vtbl, refs, ref2s), ntoi, \ + iton, defaultval } /* upb_oneofdef ***************************************************************/ struct upb_oneofdef { - upb_def base; + upb_refcounted base; + const char *name; upb_strtable ntof; upb_inttable itof; const upb_msgdef *parent; }; +extern const struct upb_refcounted_vtbl upb_oneofdef_vtbl; + #define UPB_ONEOFDEF_INIT(name, ntof, itof, refs, ref2s) \ - { UPB_DEF_INIT(name, UPB_DEF_ENUM, refs, ref2s), ntof, itof } + { UPB_REFCOUNT_INIT(&upb_oneofdef_vtbl, refs, ref2s), name, ntof, itof } /* upb_symtab *****************************************************************/ @@ -3231,9 +3844,18 @@ struct upb_symtab { upb_strtable symtab; }; -#define UPB_SYMTAB_INIT(symtab, refs, ref2s) \ - { UPB_REFCOUNT_INIT(refs, ref2s), symtab } +struct upb_filedef { + upb_refcounted base; + const char *name; + const char *package; + upb_syntax_t syntax; + + upb_inttable defs; + upb_inttable deps; +}; + +extern const struct upb_refcounted_vtbl upb_filedef_vtbl; #endif /* UPB_STATICINIT_H_ */ /* @@ -5176,267 +5798,6 @@ inline BytesHandler::~BytesHandler() {} #endif /* UPB_HANDLERS_H */ /* -** upb::Environment (upb_env) -** -** A upb::Environment provides a means for injecting malloc and an -** error-reporting callback into encoders/decoders. This allows them to be -** independent of nearly all assumptions about their actual environment. -** -** It is also a container for allocating the encoders/decoders themselves that -** insulates clients from knowing their actual size. This provides ABI -** compatibility even if the size of the objects change. And this allows the -** structure definitions to be in the .c files instead of the .h files, making -** the .h files smaller and more readable. -*/ - - -#ifndef UPB_ENV_H_ -#define UPB_ENV_H_ - -#ifdef __cplusplus -namespace upb { -class Environment; -class SeededAllocator; -} -#endif - -UPB_DECLARE_TYPE(upb::Environment, upb_env) -UPB_DECLARE_TYPE(upb::SeededAllocator, upb_seededalloc) - -typedef void *upb_alloc_func(void *ud, void *ptr, size_t oldsize, size_t size); -typedef void upb_cleanup_func(void *ud); -typedef bool upb_error_func(void *ud, const upb_status *status); - -#ifdef __cplusplus - -/* An environment is *not* thread-safe. */ -class upb::Environment { - public: - Environment(); - ~Environment(); - - /* Set a custom memory allocation function for the environment. May ONLY - * be called before any calls to Malloc()/Realloc()/AddCleanup() below. - * If this is not called, the system realloc() function will be used. - * The given user pointer "ud" will be passed to the allocation function. - * - * The allocation function will not receive corresponding "free" calls. it - * must ensure that the memory is valid for the lifetime of the Environment, - * but it may be reclaimed any time thereafter. The likely usage is that - * "ud" points to a stateful allocator, and that the allocator frees all - * memory, arena-style, when it is destroyed. In this case the allocator must - * outlive the Environment. Another possibility is that the allocation - * function returns GC-able memory that is guaranteed to be GC-rooted for the - * life of the Environment. */ - void SetAllocationFunction(upb_alloc_func* alloc, void* ud); - - template - void SetAllocator(T* allocator) { - SetAllocationFunction(allocator->GetAllocationFunction(), allocator); - } - - /* Set a custom error reporting function. */ - void SetErrorFunction(upb_error_func* func, void* ud); - - /* Set the error reporting function to simply copy the status to the given - * status and abort. */ - void ReportErrorsTo(Status* status); - - /* Returns true if all allocations and AddCleanup() calls have succeeded, - * and no errors were reported with ReportError() (except ones that recovered - * successfully). */ - bool ok() const; - - /* Functions for use by encoders/decoders. **********************************/ - - /* Reports an error to this environment's callback, returning true if - * the caller should try to recover. */ - bool ReportError(const Status* status); - - /* Allocate memory. Uses the environment's allocation function. - * - * There is no need to free(). All memory will be freed automatically, but is - * guaranteed to outlive the Environment. */ - void* Malloc(size_t size); - - /* Reallocate memory. Preserves "oldsize" bytes from the existing buffer - * Requires: oldsize <= existing_size. - * - * TODO(haberman): should we also enforce that oldsize <= size? */ - void* Realloc(void* ptr, size_t oldsize, size_t size); - - /* Add a cleanup function to run when the environment is destroyed. - * Returns false on out-of-memory. - * - * The first call to AddCleanup() after SetAllocationFunction() is guaranteed - * to return true -- this makes it possible to robustly set a cleanup handler - * for a custom allocation function. */ - bool AddCleanup(upb_cleanup_func* func, void* ud); - - /* Total number of bytes that have been allocated. It is undefined what - * Realloc() does to this counter. */ - size_t BytesAllocated() const; - - private: - UPB_DISALLOW_COPY_AND_ASSIGN(Environment) - -#else -struct upb_env { -#endif /* __cplusplus */ - - bool ok_; - size_t bytes_allocated; - - /* Alloc function. */ - upb_alloc_func *alloc; - void *alloc_ud; - - /* Error-reporting function. */ - upb_error_func *err; - void *err_ud; - - /* Userdata for default alloc func. */ - void *default_alloc_ud; - - /* Cleanup entries. Pointer to a cleanup_ent, defined in env.c */ - void *cleanup_head; - - /* For future expansion, since the size of this struct is exposed to users. */ - void *future1; - void *future2; -}; - -UPB_BEGIN_EXTERN_C - -void upb_env_init(upb_env *e); -void upb_env_uninit(upb_env *e); -void upb_env_setallocfunc(upb_env *e, upb_alloc_func *func, void *ud); -void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud); -void upb_env_reporterrorsto(upb_env *e, upb_status *status); -bool upb_env_ok(const upb_env *e); -bool upb_env_reporterror(upb_env *e, const upb_status *status); -void *upb_env_malloc(upb_env *e, size_t size); -void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size); -bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud); -size_t upb_env_bytesallocated(const upb_env *e); - -UPB_END_EXTERN_C - -#ifdef __cplusplus - -/* An allocator that allocates from an initial memory region (likely the stack) - * before falling back to another allocator. */ -class upb::SeededAllocator { - public: - SeededAllocator(void *mem, size_t len); - ~SeededAllocator(); - - /* Set a custom fallback memory allocation function for the allocator, to use - * once the initial region runs out. - * - * May ONLY be called before GetAllocationFunction(). If this is not - * called, the system realloc() will be the fallback allocator. */ - void SetFallbackAllocator(upb_alloc_func *alloc, void *ud); - - /* Gets the allocation function for this allocator. */ - upb_alloc_func* GetAllocationFunction(); - - private: - UPB_DISALLOW_COPY_AND_ASSIGN(SeededAllocator) - -#else -struct upb_seededalloc { -#endif /* __cplusplus */ - - /* Fallback alloc function. */ - upb_alloc_func *alloc; - upb_cleanup_func *alloc_cleanup; - void *alloc_ud; - bool need_cleanup; - bool returned_allocfunc; - - /* Userdata for default alloc func. */ - void *default_alloc_ud; - - /* Pointers for the initial memory region. */ - char *mem_base; - char *mem_ptr; - char *mem_limit; - - /* For future expansion, since the size of this struct is exposed to users. */ - void *future1; - void *future2; -}; - -UPB_BEGIN_EXTERN_C - -void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len); -void upb_seededalloc_uninit(upb_seededalloc *a); -void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, upb_alloc_func *func, - void *ud); -upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a); - -UPB_END_EXTERN_C - -#ifdef __cplusplus - -namespace upb { - -inline Environment::Environment() { - upb_env_init(this); -} -inline Environment::~Environment() { - upb_env_uninit(this); -} -inline void Environment::SetAllocationFunction(upb_alloc_func *alloc, - void *ud) { - upb_env_setallocfunc(this, alloc, ud); -} -inline void Environment::SetErrorFunction(upb_error_func *func, void *ud) { - upb_env_seterrorfunc(this, func, ud); -} -inline void Environment::ReportErrorsTo(Status* status) { - upb_env_reporterrorsto(this, status); -} -inline bool Environment::ok() const { - return upb_env_ok(this); -} -inline bool Environment::ReportError(const Status* status) { - return upb_env_reporterror(this, status); -} -inline void *Environment::Malloc(size_t size) { - return upb_env_malloc(this, size); -} -inline void *Environment::Realloc(void *ptr, size_t oldsize, size_t size) { - return upb_env_realloc(this, ptr, oldsize, size); -} -inline bool Environment::AddCleanup(upb_cleanup_func *func, void *ud) { - return upb_env_addcleanup(this, func, ud); -} -inline size_t Environment::BytesAllocated() const { - return upb_env_bytesallocated(this); -} - -inline SeededAllocator::SeededAllocator(void *mem, size_t len) { - upb_seededalloc_init(this, mem, len); -} -inline SeededAllocator::~SeededAllocator() { - upb_seededalloc_uninit(this); -} -inline void SeededAllocator::SetFallbackAllocator(upb_alloc_func *alloc, - void *ud) { - upb_seededalloc_setfallbackalloc(this, alloc, ud); -} -inline upb_alloc_func *SeededAllocator::GetAllocationFunction() { - return upb_seededalloc_getallocfunc(this); -} - -} /* namespace upb */ - -#endif /* __cplusplus */ - -#endif /* UPB_ENV_H_ */ -/* ** upb::Sink (upb_sink) ** upb::BytesSink (upb_bytessink) ** @@ -6140,12 +6501,17 @@ class upb::SymbolTable { * only a few messages are changing. We may want to add a way of adding a * tree of frozen defs to the symtab (perhaps an alternate constructor where * you pass the root of the tree?) */ - bool Add(Def*const* defs, int n, void* ref_donor, upb_status* status); + bool Add(Def*const* defs, size_t n, void* ref_donor, Status* status); bool Add(const std::vector& defs, void *owner, Status* status) { return Add((Def*const*)&defs[0], defs.size(), owner, status); } + /* Resolves all subdefs for messages in this file and attempts to freeze the + * file. If this succeeds, adds all the symbols to this SymbolTable + * (replacing any existing ones with the same names). */ + bool AddFile(FileDef* file, Status* s); + private: UPB_DISALLOW_POD_OPS(SymbolTable, upb::SymbolTable) }; @@ -6166,8 +6532,9 @@ const upb_def *upb_symtab_resolve(const upb_symtab *s, const char *base, const upb_def *upb_symtab_lookup(const upb_symtab *s, const char *sym); const upb_msgdef *upb_symtab_lookupmsg(const upb_symtab *s, const char *sym); const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym); -bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, - upb_status *status); +bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, size_t n, + void *ref_donor, upb_status *status); +bool upb_symtab_addfile(upb_symtab *s, upb_filedef *file, upb_status* status); /* upb_symtab_iter i; * for(upb_symtab_begin(&i, s, type); !upb_symtab_done(&i); @@ -6209,9 +6576,12 @@ inline const MessageDef *SymbolTable::LookupMessage(const char *sym) const { return upb_symtab_lookupmsg(this, sym); } inline bool SymbolTable::Add( - Def*const* defs, int n, void* ref_donor, upb_status* status) { + Def*const* defs, size_t n, void* ref_donor, Status* status) { return upb_symtab_add(this, (upb_def*const*)defs, n, ref_donor, status); } +inline bool SymbolTable::AddFile(FileDef* file, Status* s) { + return upb_symtab_addfile(this, file, s); +} } /* namespace upb */ #endif @@ -6255,14 +6625,9 @@ class upb::descriptor::Reader { /* The reader's input; this is where descriptor.proto data should be sent. */ Sink* input(); - /* Returns an array of all defs that have been parsed, and transfers ownership - * of them to "owner". The number of defs is stored in *n. Ownership of the - * returned array is retained and is invalidated by any other call into - * Reader. - * - * These defs are not frozen or resolved; they are ready to be added to a - * symtab. */ - upb::Def** GetDefs(void* owner, int* n); + /* Use to get the FileDefs that have been parsed. */ + size_t file_count() const; + FileDef* file(size_t i) const; /* Builds and returns handlers for the reader, owned by "owner." */ static Handlers* NewHandlers(const void* owner); @@ -6278,7 +6643,8 @@ UPB_BEGIN_EXTERN_C /* C API. */ upb_descreader *upb_descreader_create(upb_env *e, const upb_handlers *h); upb_sink *upb_descreader_input(upb_descreader *r); -upb_def **upb_descreader_getdefs(upb_descreader *r, void *owner, int *n); +size_t upb_descreader_filecount(const upb_descreader *r); +upb_filedef *upb_descreader_file(const upb_descreader *r, size_t i); const upb_handlers *upb_descreader_newhandlers(const void *owner); UPB_END_EXTERN_C @@ -6291,8 +6657,11 @@ inline Reader* Reader::Create(Environment* e, const Handlers *h) { return upb_descreader_create(e, h); } inline Sink* Reader::input() { return upb_descreader_input(this); } -inline upb::Def** Reader::GetDefs(void* owner, int* n) { - return upb_descreader_getdefs(this, owner, n); +inline size_t Reader::file_count() const { + return upb_descreader_filecount(this); +} +inline FileDef* Reader::file(size_t i) const { + return upb_descreader_file(this, i); } } /* namespace descriptor */ } /* namespace upb */ @@ -6305,614 +6674,290 @@ inline upb::Def** Reader::GetDefs(void* owner, int* n) { * actually storing protobufs. It only contains *defs* which * let you reflect over a protobuf *schema*. */ -/* This file was generated by upbc (the upb compiler). +/* This file was generated by upbc (the upb compiler) from the input + * file: + * + * upb/descriptor/descriptor.proto + * * Do not edit -- your changes will be discarded when the file is * regenerated. */ -#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ -#define GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ +#ifndef UPB_DESCRIPTOR_DESCRIPTOR_PROTO_UPB_H_ +#define UPB_DESCRIPTOR_DESCRIPTOR_PROTO_UPB_H_ -#ifdef __cplusplus UPB_BEGIN_EXTERN_C -#endif /* Enums */ typedef enum { - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_OPTIONAL = 1, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_REQUIRED = 2, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_REPEATED = 3 + google_protobuf_FieldDescriptorProto_LABEL_OPTIONAL = 1, + google_protobuf_FieldDescriptorProto_LABEL_REQUIRED = 2, + google_protobuf_FieldDescriptorProto_LABEL_REPEATED = 3 } google_protobuf_FieldDescriptorProto_Label; typedef enum { - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_DOUBLE = 1, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FLOAT = 2, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT64 = 3, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_UINT64 = 4, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT32 = 5, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FIXED64 = 6, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FIXED32 = 7, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_BOOL = 8, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_STRING = 9, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_GROUP = 10, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_MESSAGE = 11, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_BYTES = 12, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_UINT32 = 13, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_ENUM = 14, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SFIXED32 = 15, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SFIXED64 = 16, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SINT32 = 17, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SINT64 = 18 + google_protobuf_FieldDescriptorProto_TYPE_DOUBLE = 1, + google_protobuf_FieldDescriptorProto_TYPE_FLOAT = 2, + google_protobuf_FieldDescriptorProto_TYPE_INT64 = 3, + google_protobuf_FieldDescriptorProto_TYPE_UINT64 = 4, + google_protobuf_FieldDescriptorProto_TYPE_INT32 = 5, + google_protobuf_FieldDescriptorProto_TYPE_FIXED64 = 6, + google_protobuf_FieldDescriptorProto_TYPE_FIXED32 = 7, + google_protobuf_FieldDescriptorProto_TYPE_BOOL = 8, + google_protobuf_FieldDescriptorProto_TYPE_STRING = 9, + google_protobuf_FieldDescriptorProto_TYPE_GROUP = 10, + google_protobuf_FieldDescriptorProto_TYPE_MESSAGE = 11, + google_protobuf_FieldDescriptorProto_TYPE_BYTES = 12, + google_protobuf_FieldDescriptorProto_TYPE_UINT32 = 13, + google_protobuf_FieldDescriptorProto_TYPE_ENUM = 14, + google_protobuf_FieldDescriptorProto_TYPE_SFIXED32 = 15, + google_protobuf_FieldDescriptorProto_TYPE_SFIXED64 = 16, + google_protobuf_FieldDescriptorProto_TYPE_SINT32 = 17, + google_protobuf_FieldDescriptorProto_TYPE_SINT64 = 18 } google_protobuf_FieldDescriptorProto_Type; typedef enum { - GOOGLE_PROTOBUF_FIELDOPTIONS_STRING = 0, - GOOGLE_PROTOBUF_FIELDOPTIONS_CORD = 1, - GOOGLE_PROTOBUF_FIELDOPTIONS_STRING_PIECE = 2 + google_protobuf_FieldOptions_STRING = 0, + google_protobuf_FieldOptions_CORD = 1, + google_protobuf_FieldOptions_STRING_PIECE = 2 } google_protobuf_FieldOptions_CType; typedef enum { - GOOGLE_PROTOBUF_FIELDOPTIONS_JS_NORMAL = 0, - GOOGLE_PROTOBUF_FIELDOPTIONS_JS_STRING = 1, - GOOGLE_PROTOBUF_FIELDOPTIONS_JS_NUMBER = 2 + google_protobuf_FieldOptions_JS_NORMAL = 0, + google_protobuf_FieldOptions_JS_STRING = 1, + google_protobuf_FieldOptions_JS_NUMBER = 2 } google_protobuf_FieldOptions_JSType; typedef enum { - GOOGLE_PROTOBUF_FILEOPTIONS_SPEED = 1, - GOOGLE_PROTOBUF_FILEOPTIONS_CODE_SIZE = 2, - GOOGLE_PROTOBUF_FILEOPTIONS_LITE_RUNTIME = 3 + google_protobuf_FileOptions_SPEED = 1, + google_protobuf_FileOptions_CODE_SIZE = 2, + google_protobuf_FileOptions_LITE_RUNTIME = 3 } google_protobuf_FileOptions_OptimizeMode; -/* Selectors */ +/* MessageDefs: call these functions to get a ref to a msgdef. */ +const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ReservedRange_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_EnumDescriptorProto_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_EnumOptions_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_EnumValueDescriptorProto_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_EnumValueOptions_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_FieldDescriptorProto_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_FieldOptions_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_FileDescriptorProto_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_FileDescriptorSet_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_FileOptions_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_MessageOptions_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_MethodDescriptorProto_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_MethodOptions_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_OneofDescriptorProto_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_ServiceDescriptorProto_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_ServiceOptions_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo_Location_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption_get(const void *owner); +const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption_NamePart_get(const void *owner); -/* google.protobuf.DescriptorProto */ -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_STARTSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_STARTSUBMSG 4 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_STARTSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_STARTSUBMSG 6 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_OPTIONS_STARTSUBMSG 7 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ONEOF_DECL_STARTSUBMSG 8 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_RESERVED_RANGE_STARTSUBMSG 9 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_STARTSEQ 10 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_ENDSEQ 11 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_ENDSUBMSG 12 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_STARTSEQ 13 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_ENDSEQ 14 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_ENDSUBMSG 15 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_STARTSEQ 16 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_ENDSEQ 17 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_ENDSUBMSG 18 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_STARTSEQ 19 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_ENDSEQ 20 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_ENDSUBMSG 21 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_STARTSEQ 22 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_ENDSEQ 23 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_ENDSUBMSG 24 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_OPTIONS_ENDSUBMSG 25 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ONEOF_DECL_STARTSEQ 26 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ONEOF_DECL_ENDSEQ 27 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ONEOF_DECL_ENDSUBMSG 28 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_RESERVED_RANGE_STARTSEQ 29 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_RESERVED_RANGE_ENDSEQ 30 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_RESERVED_RANGE_ENDSUBMSG 31 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_STRING 32 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_STARTSTR 33 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_ENDSTR 34 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_RESERVED_NAME_STARTSEQ 35 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_RESERVED_NAME_ENDSEQ 36 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_RESERVED_NAME_STRING 37 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_RESERVED_NAME_STARTSTR 38 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_RESERVED_NAME_ENDSTR 39 +/* EnumDefs: call these functions to get a ref to an enumdef. */ +const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Label_get(const void *owner); +const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Type_get(const void *owner); +const upb_enumdef *upbdefs_google_protobuf_FieldOptions_CType_get(const void *owner); +const upb_enumdef *upbdefs_google_protobuf_FieldOptions_JSType_get(const void *owner); +const upb_enumdef *upbdefs_google_protobuf_FileOptions_OptimizeMode_get(const void *owner); -/* google.protobuf.DescriptorProto.ExtensionRange */ -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSIONRANGE_START_INT32 2 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSIONRANGE_END_INT32 3 - -/* google.protobuf.DescriptorProto.ReservedRange */ -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_RESERVEDRANGE_START_INT32 2 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_RESERVEDRANGE_END_INT32 3 - -/* google.protobuf.EnumDescriptorProto */ -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_STARTSEQ 4 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_ENDSEQ 5 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_ENDSUBMSG 6 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 7 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_STRING 8 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_STARTSTR 9 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_ENDSTR 10 - -/* google.protobuf.EnumOptions */ -#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_ALLOW_ALIAS_BOOL 6 -#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_DEPRECATED_BOOL 7 - -/* google.protobuf.EnumValueDescriptorProto */ -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_STRING 4 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_STARTSTR 5 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_ENDSTR 6 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NUMBER_INT32 7 - -/* google.protobuf.EnumValueOptions */ -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_DEPRECATED_BOOL 6 - -/* google.protobuf.FieldDescriptorProto */ -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_STRING 4 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_STARTSTR 5 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_ENDSTR 6 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_STRING 7 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_STARTSTR 8 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_ENDSTR 9 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NUMBER_INT32 10 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_INT32 11 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT32 12 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_STRING 13 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_STARTSTR 14 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_ENDSTR 15 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_STRING 16 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_STARTSTR 17 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_ENDSTR 18 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_ONEOF_INDEX_INT32 19 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_JSON_NAME_STRING 20 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_JSON_NAME_STARTSTR 21 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_JSON_NAME_ENDSTR 22 - -/* google.protobuf.FieldOptions */ -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_CTYPE_INT32 6 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_PACKED_BOOL 7 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_DEPRECATED_BOOL 8 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_LAZY_BOOL 9 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_JSTYPE_INT32 10 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_WEAK_BOOL 11 - -/* google.protobuf.FileDescriptorProto */ -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_STARTSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_STARTSUBMSG 4 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_STARTSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 6 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SOURCE_CODE_INFO_STARTSUBMSG 7 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_STARTSEQ 8 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_ENDSEQ 9 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_ENDSUBMSG 10 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_STARTSEQ 11 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_ENDSEQ 12 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_ENDSUBMSG 13 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_STARTSEQ 14 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_ENDSEQ 15 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_ENDSUBMSG 16 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_STARTSEQ 17 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_ENDSEQ 18 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_ENDSUBMSG 19 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 20 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SOURCE_CODE_INFO_ENDSUBMSG 21 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_STRING 22 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_STARTSTR 23 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_ENDSTR 24 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_STRING 25 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_STARTSTR 26 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_ENDSTR 27 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STARTSEQ 28 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_ENDSEQ 29 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STRING 30 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STARTSTR 31 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_ENDSTR 32 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_STARTSEQ 33 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_ENDSEQ 34 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_INT32 35 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_STARTSEQ 36 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_ENDSEQ 37 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_INT32 38 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SYNTAX_STRING 39 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SYNTAX_STARTSTR 40 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SYNTAX_ENDSTR 41 - -/* google.protobuf.FileDescriptorSet */ -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_ENDSUBMSG 5 - -/* google.protobuf.FileOptions */ -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_STRING 6 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_STARTSTR 7 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_ENDSTR 8 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_STRING 9 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_STARTSTR 10 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_ENDSTR 11 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_OPTIMIZE_FOR_INT32 12 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_MULTIPLE_FILES_BOOL 13 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_STRING 14 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_STARTSTR 15 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_ENDSTR 16 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_CC_GENERIC_SERVICES_BOOL 17 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_GENERIC_SERVICES_BOOL 18 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_PY_GENERIC_SERVICES_BOOL 19 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_GENERATE_EQUALS_AND_HASH_BOOL 20 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_DEPRECATED_BOOL 21 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_STRING_CHECK_UTF8_BOOL 22 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_CC_ENABLE_ARENAS_BOOL 23 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_OBJC_CLASS_PREFIX_STRING 24 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_OBJC_CLASS_PREFIX_STARTSTR 25 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_OBJC_CLASS_PREFIX_ENDSTR 26 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_CSHARP_NAMESPACE_STRING 27 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_CSHARP_NAMESPACE_STARTSTR 28 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_CSHARP_NAMESPACE_ENDSTR 29 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVANANO_USE_DEPRECATED_PACKAGE_BOOL 30 - -/* google.protobuf.MessageOptions */ -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_MESSAGE_SET_WIRE_FORMAT_BOOL 6 -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_NO_STANDARD_DESCRIPTOR_ACCESSOR_BOOL 7 -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_DEPRECATED_BOOL 8 -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_MAP_ENTRY_BOOL 9 - -/* google.protobuf.MethodDescriptorProto */ -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_STRING 4 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_STARTSTR 5 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_ENDSTR 6 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_STRING 7 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_STARTSTR 8 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_ENDSTR 9 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_STRING 10 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_STARTSTR 11 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_ENDSTR 12 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_CLIENT_STREAMING_BOOL 13 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_SERVER_STREAMING_BOOL 14 - -/* google.protobuf.MethodOptions */ -#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_DEPRECATED_BOOL 6 - -/* google.protobuf.OneofDescriptorProto */ -#define SEL_GOOGLE_PROTOBUF_ONEOFDESCRIPTORPROTO_NAME_STRING 2 -#define SEL_GOOGLE_PROTOBUF_ONEOFDESCRIPTORPROTO_NAME_STARTSTR 3 -#define SEL_GOOGLE_PROTOBUF_ONEOFDESCRIPTORPROTO_NAME_ENDSTR 4 - -/* google.protobuf.ServiceDescriptorProto */ -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_STARTSEQ 4 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_ENDSEQ 5 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_ENDSUBMSG 6 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 7 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_STRING 8 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_STARTSTR 9 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_ENDSTR 10 - -/* google.protobuf.ServiceOptions */ -#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_DEPRECATED_BOOL 6 - -/* google.protobuf.SourceCodeInfo */ -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_ENDSUBMSG 5 - -/* google.protobuf.SourceCodeInfo.Location */ -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_STARTSEQ 2 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_ENDSEQ 3 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_INT32 4 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_STARTSEQ 5 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_ENDSEQ 6 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_INT32 7 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_STRING 8 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_STARTSTR 9 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_ENDSTR 10 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_STRING 11 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_STARTSTR 12 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_ENDSTR 13 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_DETACHED_COMMENTS_STARTSEQ 14 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_DETACHED_COMMENTS_ENDSEQ 15 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_DETACHED_COMMENTS_STRING 16 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_DETACHED_COMMENTS_STARTSTR 17 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_DETACHED_COMMENTS_ENDSTR 18 - -/* google.protobuf.UninterpretedOption */ -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_ENDSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_STRING 6 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_STARTSTR 7 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_ENDSTR 8 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_POSITIVE_INT_VALUE_UINT64 9 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NEGATIVE_INT_VALUE_INT64 10 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_DOUBLE_VALUE_DOUBLE 11 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_STRING 12 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_STARTSTR 13 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_ENDSTR 14 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_STRING 15 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_STARTSTR 16 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_ENDSTR 17 - -/* google.protobuf.UninterpretedOption.NamePart */ -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_STRING 2 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_STARTSTR 3 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_ENDSTR 4 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_IS_EXTENSION_BOOL 5 - -const upb_symtab *upbdefs_google_protobuf_descriptor(const void *owner); - -/* MessageDefs */ -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_DescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto"); - assert(m); - return m; +/* Functions to test whether this message is of a certain type. */ +UPB_INLINE bool upbdefs_google_protobuf_DescriptorProto_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.DescriptorProto") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto.ExtensionRange"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_DescriptorProto_ExtensionRange_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.DescriptorProto.ExtensionRange") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ReservedRange(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto.ReservedRange"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_DescriptorProto_ReservedRange_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.DescriptorProto.ReservedRange") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumDescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumDescriptorProto"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_EnumDescriptorProto_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.EnumDescriptorProto") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumOptions"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_EnumOptions_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.EnumOptions") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumValueDescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumValueDescriptorProto"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_EnumValueDescriptorProto_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.EnumValueDescriptorProto") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumValueOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumValueOptions"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_EnumValueOptions_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.EnumValueOptions") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FieldDescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FieldDescriptorProto"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_FieldDescriptorProto_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.FieldDescriptorProto") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FieldOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FieldOptions"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_FieldOptions_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.FieldOptions") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileDescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileDescriptorProto"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_FileDescriptorProto_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.FileDescriptorProto") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileDescriptorSet(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileDescriptorSet"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_FileDescriptorSet_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.FileDescriptorSet") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileOptions"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_FileOptions_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.FileOptions") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MessageOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MessageOptions"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_MessageOptions_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.MessageOptions") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MethodDescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MethodDescriptorProto"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_MethodDescriptorProto_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.MethodDescriptorProto") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MethodOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MethodOptions"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_MethodOptions_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.MethodOptions") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_OneofDescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.OneofDescriptorProto"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_OneofDescriptorProto_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.OneofDescriptorProto") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_ServiceDescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.ServiceDescriptorProto"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_ServiceDescriptorProto_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.ServiceDescriptorProto") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_ServiceOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.ServiceOptions"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_ServiceOptions_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.ServiceOptions") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.SourceCodeInfo"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_SourceCodeInfo_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.SourceCodeInfo") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo_Location(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.SourceCodeInfo.Location"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_SourceCodeInfo_Location_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.SourceCodeInfo.Location") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.UninterpretedOption"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_UninterpretedOption_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.UninterpretedOption") == 0; } -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption_NamePart(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.UninterpretedOption.NamePart"); - assert(m); - return m; +UPB_INLINE bool upbdefs_google_protobuf_UninterpretedOption_NamePart_is(const upb_msgdef *m) { + return strcmp(upb_msgdef_fullname(m), "google.protobuf.UninterpretedOption.NamePart") == 0; +} + +/* Functions to test whether this enum is of a certain type. */ +UPB_INLINE bool upbdefs_google_protobuf_FieldDescriptorProto_Label_is(const upb_enumdef *e) { + return strcmp(upb_enumdef_fullname(e), "google.protobuf.FieldDescriptorProto.Label") == 0; +} +UPB_INLINE bool upbdefs_google_protobuf_FieldDescriptorProto_Type_is(const upb_enumdef *e) { + return strcmp(upb_enumdef_fullname(e), "google.protobuf.FieldDescriptorProto.Type") == 0; +} +UPB_INLINE bool upbdefs_google_protobuf_FieldOptions_CType_is(const upb_enumdef *e) { + return strcmp(upb_enumdef_fullname(e), "google.protobuf.FieldOptions.CType") == 0; +} +UPB_INLINE bool upbdefs_google_protobuf_FieldOptions_JSType_is(const upb_enumdef *e) { + return strcmp(upb_enumdef_fullname(e), "google.protobuf.FieldOptions.JSType") == 0; +} +UPB_INLINE bool upbdefs_google_protobuf_FileOptions_OptimizeMode_is(const upb_enumdef *e) { + return strcmp(upb_enumdef_fullname(e), "google.protobuf.FileOptions.OptimizeMode") == 0; } -/* EnumDefs */ -UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Label(const upb_symtab *s) { - const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldDescriptorProto.Label"); - assert(e); - return e; -} -UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Type(const upb_symtab *s) { - const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldDescriptorProto.Type"); - assert(e); - return e; -} -UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldOptions_CType(const upb_symtab *s) { - const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldOptions.CType"); - assert(e); - return e; -} -UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldOptions_JSType(const upb_symtab *s) { - const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldOptions.JSType"); - assert(e); - return e; -} -UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FileOptions_OptimizeMode(const upb_symtab *s) { - const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FileOptions.OptimizeMode"); - assert(e); - return e; -} - -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_end(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto_ExtensionRange(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_start(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto_ExtensionRange(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ReservedRange_end(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto_ReservedRange(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ReservedRange_start(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto_ReservedRange(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_enum_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 4); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 6); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_extension_range(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 5); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_field(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_nested_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_oneof_decl(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 8); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 7); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_reserved_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 10); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_reserved_range(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 9); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_allow_alias(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumOptions(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_deprecated(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumOptions(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_number(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueOptions_deprecated(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueOptions(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_default_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 7); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_extendee(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_json_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 10); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_label(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 4); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_number(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_oneof_index(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 9); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 8); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 5); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_type_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 6); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_ctype(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_deprecated(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_jstype(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 6); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_lazy(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 5); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_packed(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_weak(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 10); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_enum_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 5); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 7); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_message_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 4); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 8); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_public_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 10); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_service(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 6); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_source_code_info(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 9); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_syntax(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 12); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_weak_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 11); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorSet_file(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorSet(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_cc_enable_arenas(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 31); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_cc_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 16); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_csharp_namespace(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 37); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_deprecated(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 23); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_go_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 11); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_generate_equals_and_hash(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 20); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 17); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_multiple_files(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 10); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_outer_classname(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 8); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_string_check_utf8(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 27); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_javanano_use_deprecated_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 38); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_objc_class_prefix(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 36); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_optimize_for(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 9); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_py_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 18); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_deprecated(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_map_entry(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 7); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_message_set_wire_format(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_no_standard_descriptor_accessor(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_client_streaming(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 5); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_input_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 4); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_output_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_server_streaming(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 6); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodOptions_deprecated(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodOptions(s), 33); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_OneofDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_OneofDescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_method(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceOptions_deprecated(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceOptions(s), 33); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_leading_comments(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_leading_detached_comments(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 6); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_path(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_span(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_trailing_comments(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 4); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_location(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_is_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption_NamePart(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_name_part(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption_NamePart(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_aggregate_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 8); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_double_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 6); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_identifier_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_negative_int_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 5); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_positive_int_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 4); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_string_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 7); } +/* Functions to get a fielddef from a msgdef reference. */ +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_f_end(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_ExtensionRange_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_f_start(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_ExtensionRange_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ReservedRange_f_end(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_ReservedRange_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ReservedRange_f_start(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_ReservedRange_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_enum_type(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_extension(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_extension_range(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_field(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_name(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_nested_type(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_oneof_decl(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 8); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_options(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 7); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_reserved_name(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 10); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_f_reserved_range(const upb_msgdef *m) { assert(upbdefs_google_protobuf_DescriptorProto_is(m)); return upb_msgdef_itof(m, 9); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_f_name(const upb_msgdef *m) { assert(upbdefs_google_protobuf_EnumDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_f_options(const upb_msgdef *m) { assert(upbdefs_google_protobuf_EnumDescriptorProto_is(m)); return upb_msgdef_itof(m, 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_f_value(const upb_msgdef *m) { assert(upbdefs_google_protobuf_EnumDescriptorProto_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_f_allow_alias(const upb_msgdef *m) { assert(upbdefs_google_protobuf_EnumOptions_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_f_deprecated(const upb_msgdef *m) { assert(upbdefs_google_protobuf_EnumOptions_is(m)); return upb_msgdef_itof(m, 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_f_uninterpreted_option(const upb_msgdef *m) { assert(upbdefs_google_protobuf_EnumOptions_is(m)); return upb_msgdef_itof(m, 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_f_name(const upb_msgdef *m) { assert(upbdefs_google_protobuf_EnumValueDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_f_number(const upb_msgdef *m) { assert(upbdefs_google_protobuf_EnumValueDescriptorProto_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_f_options(const upb_msgdef *m) { assert(upbdefs_google_protobuf_EnumValueDescriptorProto_is(m)); return upb_msgdef_itof(m, 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueOptions_f_deprecated(const upb_msgdef *m) { assert(upbdefs_google_protobuf_EnumValueOptions_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueOptions_f_uninterpreted_option(const upb_msgdef *m) { assert(upbdefs_google_protobuf_EnumValueOptions_is(m)); return upb_msgdef_itof(m, 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_default_value(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 7); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_extendee(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_json_name(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 10); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_label(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_name(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_number(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_oneof_index(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 9); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_options(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 8); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_type(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_f_type_name(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); return upb_msgdef_itof(m, 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_ctype(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_deprecated(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_jstype(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_lazy(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_packed(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_uninterpreted_option(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_f_weak(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FieldOptions_is(m)); return upb_msgdef_itof(m, 10); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_dependency(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_enum_type(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_extension(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 7); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_message_type(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_name(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_options(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 8); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_package(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_public_dependency(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 10); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_service(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_source_code_info(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 9); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_syntax(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 12); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_f_weak_dependency(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); return upb_msgdef_itof(m, 11); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorSet_f_file(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileDescriptorSet_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_cc_enable_arenas(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 31); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_cc_generic_services(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 16); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_csharp_namespace(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 37); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_deprecated(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 23); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_go_package(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 11); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_java_generate_equals_and_hash(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 20); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_java_generic_services(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 17); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_java_multiple_files(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 10); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_java_outer_classname(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 8); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_java_package(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_java_string_check_utf8(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 27); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_javanano_use_deprecated_package(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 38); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_objc_class_prefix(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 36); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_optimize_for(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 9); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_py_generic_services(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 18); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_f_uninterpreted_option(const upb_msgdef *m) { assert(upbdefs_google_protobuf_FileOptions_is(m)); return upb_msgdef_itof(m, 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_f_deprecated(const upb_msgdef *m) { assert(upbdefs_google_protobuf_MessageOptions_is(m)); return upb_msgdef_itof(m, 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_f_map_entry(const upb_msgdef *m) { assert(upbdefs_google_protobuf_MessageOptions_is(m)); return upb_msgdef_itof(m, 7); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_f_message_set_wire_format(const upb_msgdef *m) { assert(upbdefs_google_protobuf_MessageOptions_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_f_no_standard_descriptor_accessor(const upb_msgdef *m) { assert(upbdefs_google_protobuf_MessageOptions_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_f_uninterpreted_option(const upb_msgdef *m) { assert(upbdefs_google_protobuf_MessageOptions_is(m)); return upb_msgdef_itof(m, 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_f_client_streaming(const upb_msgdef *m) { assert(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); return upb_msgdef_itof(m, 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_f_input_type(const upb_msgdef *m) { assert(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_f_name(const upb_msgdef *m) { assert(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_f_options(const upb_msgdef *m) { assert(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); return upb_msgdef_itof(m, 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_f_output_type(const upb_msgdef *m) { assert(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); return upb_msgdef_itof(m, 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_f_server_streaming(const upb_msgdef *m) { assert(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); return upb_msgdef_itof(m, 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodOptions_f_deprecated(const upb_msgdef *m) { assert(upbdefs_google_protobuf_MethodOptions_is(m)); return upb_msgdef_itof(m, 33); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodOptions_f_uninterpreted_option(const upb_msgdef *m) { assert(upbdefs_google_protobuf_MethodOptions_is(m)); return upb_msgdef_itof(m, 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_OneofDescriptorProto_f_name(const upb_msgdef *m) { assert(upbdefs_google_protobuf_OneofDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_f_method(const upb_msgdef *m) { assert(upbdefs_google_protobuf_ServiceDescriptorProto_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_f_name(const upb_msgdef *m) { assert(upbdefs_google_protobuf_ServiceDescriptorProto_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_f_options(const upb_msgdef *m) { assert(upbdefs_google_protobuf_ServiceDescriptorProto_is(m)); return upb_msgdef_itof(m, 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceOptions_f_deprecated(const upb_msgdef *m) { assert(upbdefs_google_protobuf_ServiceOptions_is(m)); return upb_msgdef_itof(m, 33); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceOptions_f_uninterpreted_option(const upb_msgdef *m) { assert(upbdefs_google_protobuf_ServiceOptions_is(m)); return upb_msgdef_itof(m, 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_f_leading_comments(const upb_msgdef *m) { assert(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m)); return upb_msgdef_itof(m, 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_f_leading_detached_comments(const upb_msgdef *m) { assert(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m)); return upb_msgdef_itof(m, 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_f_path(const upb_msgdef *m) { assert(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_f_span(const upb_msgdef *m) { assert(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_f_trailing_comments(const upb_msgdef *m) { assert(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m)); return upb_msgdef_itof(m, 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_f_location(const upb_msgdef *m) { assert(upbdefs_google_protobuf_SourceCodeInfo_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_f_is_extension(const upb_msgdef *m) { assert(upbdefs_google_protobuf_UninterpretedOption_NamePart_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_f_name_part(const upb_msgdef *m) { assert(upbdefs_google_protobuf_UninterpretedOption_NamePart_is(m)); return upb_msgdef_itof(m, 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_aggregate_value(const upb_msgdef *m) { assert(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 8); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_double_value(const upb_msgdef *m) { assert(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_identifier_value(const upb_msgdef *m) { assert(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_name(const upb_msgdef *m) { assert(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_negative_int_value(const upb_msgdef *m) { assert(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_positive_int_value(const upb_msgdef *m) { assert(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_f_string_value(const upb_msgdef *m) { assert(upbdefs_google_protobuf_UninterpretedOption_is(m)); return upb_msgdef_itof(m, 7); } UPB_END_EXTERN_C @@ -6921,320 +6966,360 @@ UPB_END_EXTERN_C namespace upbdefs { namespace google { namespace protobuf { -namespace descriptor { -inline upb::reffed_ptr SymbolTable() { - const upb::SymbolTable* s = upbdefs_google_protobuf_descriptor(&s); - return upb::reffed_ptr(s, &s); -} -} /* namespace descriptor */ + +class DescriptorProto : public ::upb::reffed_ptr { + public: + DescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_DescriptorProto_is(m)); + } + + static DescriptorProto get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_get(&m); + return DescriptorProto(m, &m); + } + + class ExtensionRange : public ::upb::reffed_ptr { + public: + ExtensionRange(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_DescriptorProto_ExtensionRange_is(m)); + } + + static ExtensionRange get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_ExtensionRange_get(&m); + return ExtensionRange(m, &m); + } + }; + + class ReservedRange : public ::upb::reffed_ptr { + public: + ReservedRange(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_DescriptorProto_ReservedRange_is(m)); + } + + static ReservedRange get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_DescriptorProto_ReservedRange_get(&m); + return ReservedRange(m, &m); + } + }; +}; + +class EnumDescriptorProto : public ::upb::reffed_ptr { + public: + EnumDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_EnumDescriptorProto_is(m)); + } + + static EnumDescriptorProto get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumDescriptorProto_get(&m); + return EnumDescriptorProto(m, &m); + } +}; + +class EnumOptions : public ::upb::reffed_ptr { + public: + EnumOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_EnumOptions_is(m)); + } + + static EnumOptions get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumOptions_get(&m); + return EnumOptions(m, &m); + } +}; + +class EnumValueDescriptorProto : public ::upb::reffed_ptr { + public: + EnumValueDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_EnumValueDescriptorProto_is(m)); + } + + static EnumValueDescriptorProto get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumValueDescriptorProto_get(&m); + return EnumValueDescriptorProto(m, &m); + } +}; + +class EnumValueOptions : public ::upb::reffed_ptr { + public: + EnumValueOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_EnumValueOptions_is(m)); + } + + static EnumValueOptions get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_EnumValueOptions_get(&m); + return EnumValueOptions(m, &m); + } +}; + +class FieldDescriptorProto : public ::upb::reffed_ptr { + public: + FieldDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_FieldDescriptorProto_is(m)); + } + + static FieldDescriptorProto get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_FieldDescriptorProto_get(&m); + return FieldDescriptorProto(m, &m); + } + + class Label : public ::upb::reffed_ptr { + public: + Label(const ::upb::EnumDef* e, const void *ref_donor = NULL) + : reffed_ptr(e, ref_donor) { + assert(upbdefs_google_protobuf_FieldDescriptorProto_Label_is(e)); + } + static Label get() { + const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldDescriptorProto_Label_get(&e); + return Label(e, &e); + } + }; + + class Type : public ::upb::reffed_ptr { + public: + Type(const ::upb::EnumDef* e, const void *ref_donor = NULL) + : reffed_ptr(e, ref_donor) { + assert(upbdefs_google_protobuf_FieldDescriptorProto_Type_is(e)); + } + static Type get() { + const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldDescriptorProto_Type_get(&e); + return Type(e, &e); + } + }; +}; + +class FieldOptions : public ::upb::reffed_ptr { + public: + FieldOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_FieldOptions_is(m)); + } + + static FieldOptions get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_FieldOptions_get(&m); + return FieldOptions(m, &m); + } + + class CType : public ::upb::reffed_ptr { + public: + CType(const ::upb::EnumDef* e, const void *ref_donor = NULL) + : reffed_ptr(e, ref_donor) { + assert(upbdefs_google_protobuf_FieldOptions_CType_is(e)); + } + static CType get() { + const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldOptions_CType_get(&e); + return CType(e, &e); + } + }; + + class JSType : public ::upb::reffed_ptr { + public: + JSType(const ::upb::EnumDef* e, const void *ref_donor = NULL) + : reffed_ptr(e, ref_donor) { + assert(upbdefs_google_protobuf_FieldOptions_JSType_is(e)); + } + static JSType get() { + const ::upb::EnumDef* e = upbdefs_google_protobuf_FieldOptions_JSType_get(&e); + return JSType(e, &e); + } + }; +}; + +class FileDescriptorProto : public ::upb::reffed_ptr { + public: + FileDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_FileDescriptorProto_is(m)); + } + + static FileDescriptorProto get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_FileDescriptorProto_get(&m); + return FileDescriptorProto(m, &m); + } +}; + +class FileDescriptorSet : public ::upb::reffed_ptr { + public: + FileDescriptorSet(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_FileDescriptorSet_is(m)); + } + + static FileDescriptorSet get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_FileDescriptorSet_get(&m); + return FileDescriptorSet(m, &m); + } +}; + +class FileOptions : public ::upb::reffed_ptr { + public: + FileOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_FileOptions_is(m)); + } + + static FileOptions get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_FileOptions_get(&m); + return FileOptions(m, &m); + } + + class OptimizeMode : public ::upb::reffed_ptr { + public: + OptimizeMode(const ::upb::EnumDef* e, const void *ref_donor = NULL) + : reffed_ptr(e, ref_donor) { + assert(upbdefs_google_protobuf_FileOptions_OptimizeMode_is(e)); + } + static OptimizeMode get() { + const ::upb::EnumDef* e = upbdefs_google_protobuf_FileOptions_OptimizeMode_get(&e); + return OptimizeMode(e, &e); + } + }; +}; + +class MessageOptions : public ::upb::reffed_ptr { + public: + MessageOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_MessageOptions_is(m)); + } + + static MessageOptions get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_MessageOptions_get(&m); + return MessageOptions(m, &m); + } +}; + +class MethodDescriptorProto : public ::upb::reffed_ptr { + public: + MethodDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_MethodDescriptorProto_is(m)); + } + + static MethodDescriptorProto get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_MethodDescriptorProto_get(&m); + return MethodDescriptorProto(m, &m); + } +}; + +class MethodOptions : public ::upb::reffed_ptr { + public: + MethodOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_MethodOptions_is(m)); + } + + static MethodOptions get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_MethodOptions_get(&m); + return MethodOptions(m, &m); + } +}; + +class OneofDescriptorProto : public ::upb::reffed_ptr { + public: + OneofDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_OneofDescriptorProto_is(m)); + } + + static OneofDescriptorProto get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_OneofDescriptorProto_get(&m); + return OneofDescriptorProto(m, &m); + } +}; + +class ServiceDescriptorProto : public ::upb::reffed_ptr { + public: + ServiceDescriptorProto(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_ServiceDescriptorProto_is(m)); + } + + static ServiceDescriptorProto get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_ServiceDescriptorProto_get(&m); + return ServiceDescriptorProto(m, &m); + } +}; + +class ServiceOptions : public ::upb::reffed_ptr { + public: + ServiceOptions(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_ServiceOptions_is(m)); + } + + static ServiceOptions get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_ServiceOptions_get(&m); + return ServiceOptions(m, &m); + } +}; + +class SourceCodeInfo : public ::upb::reffed_ptr { + public: + SourceCodeInfo(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_SourceCodeInfo_is(m)); + } + + static SourceCodeInfo get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_SourceCodeInfo_get(&m); + return SourceCodeInfo(m, &m); + } + + class Location : public ::upb::reffed_ptr { + public: + Location(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_SourceCodeInfo_Location_is(m)); + } + + static Location get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_SourceCodeInfo_Location_get(&m); + return Location(m, &m); + } + }; +}; + +class UninterpretedOption : public ::upb::reffed_ptr { + public: + UninterpretedOption(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_UninterpretedOption_is(m)); + } + + static UninterpretedOption get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_UninterpretedOption_get(&m); + return UninterpretedOption(m, &m); + } + + class NamePart : public ::upb::reffed_ptr { + public: + NamePart(const ::upb::MessageDef* m, const void *ref_donor = NULL) + : reffed_ptr(m, ref_donor) { + assert(upbdefs_google_protobuf_UninterpretedOption_NamePart_is(m)); + } + + static NamePart get() { + const ::upb::MessageDef* m = upbdefs_google_protobuf_UninterpretedOption_NamePart_get(&m); + return NamePart(m, &m); + } + }; +}; + } /* namespace protobuf */ } /* namespace google */ - -#define RETURN_REFFED(type, func) \ - const type* obj = func(upbdefs::google::protobuf::descriptor::SymbolTable().get()); \ - return upb::reffed_ptr(obj); - -namespace google { -namespace protobuf { -namespace DescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_DescriptorProto) } -inline upb::reffed_ptr enum_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_enum_type) } -inline upb::reffed_ptr extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_extension) } -inline upb::reffed_ptr extension_range() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_extension_range) } -inline upb::reffed_ptr field() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_field) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_name) } -inline upb::reffed_ptr nested_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_nested_type) } -inline upb::reffed_ptr oneof_decl() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_oneof_decl) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_options) } -inline upb::reffed_ptr reserved_name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_reserved_name) } -inline upb::reffed_ptr reserved_range() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_reserved_range) } -} /* namespace DescriptorProto */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace DescriptorProto { -namespace ExtensionRange { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange) } -inline upb::reffed_ptr end() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange_end) } -inline upb::reffed_ptr start() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange_start) } -} /* namespace ExtensionRange */ -} /* namespace DescriptorProto */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace DescriptorProto { -namespace ReservedRange { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_DescriptorProto_ReservedRange) } -inline upb::reffed_ptr end() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_ReservedRange_end) } -inline upb::reffed_ptr start() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_ReservedRange_start) } -} /* namespace ReservedRange */ -} /* namespace DescriptorProto */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace EnumDescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumDescriptorProto) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_name) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_options) } -inline upb::reffed_ptr value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_value) } -} /* namespace EnumDescriptorProto */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace EnumOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumOptions) } -inline upb::reffed_ptr allow_alias() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumOptions_allow_alias) } -inline upb::reffed_ptr deprecated() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumOptions_deprecated) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumOptions_uninterpreted_option) } -} /* namespace EnumOptions */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace EnumValueDescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumValueDescriptorProto) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_name) } -inline upb::reffed_ptr number() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_number) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_options) } -} /* namespace EnumValueDescriptorProto */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace EnumValueOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumValueOptions) } -inline upb::reffed_ptr deprecated() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueOptions_deprecated) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueOptions_uninterpreted_option) } -} /* namespace EnumValueOptions */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace FieldDescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FieldDescriptorProto) } -inline upb::reffed_ptr default_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_default_value) } -inline upb::reffed_ptr extendee() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_extendee) } -inline upb::reffed_ptr json_name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_json_name) } -inline upb::reffed_ptr label() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_label) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_name) } -inline upb::reffed_ptr number() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_number) } -inline upb::reffed_ptr oneof_index() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_oneof_index) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_options) } -inline upb::reffed_ptr type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_type) } -inline upb::reffed_ptr type_name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_type_name) } -inline upb::reffed_ptr Label() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldDescriptorProto_Label) } -inline upb::reffed_ptr Type() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldDescriptorProto_Type) } -} /* namespace FieldDescriptorProto */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace FieldOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FieldOptions) } -inline upb::reffed_ptr ctype() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_ctype) } -inline upb::reffed_ptr deprecated() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_deprecated) } -inline upb::reffed_ptr jstype() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_jstype) } -inline upb::reffed_ptr lazy() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_lazy) } -inline upb::reffed_ptr packed() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_packed) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_uninterpreted_option) } -inline upb::reffed_ptr weak() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_weak) } -inline upb::reffed_ptr CType() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldOptions_CType) } -inline upb::reffed_ptr JSType() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldOptions_JSType) } -} /* namespace FieldOptions */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace FileDescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileDescriptorProto) } -inline upb::reffed_ptr dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_dependency) } -inline upb::reffed_ptr enum_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_enum_type) } -inline upb::reffed_ptr extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_extension) } -inline upb::reffed_ptr message_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_message_type) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_name) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_options) } -inline upb::reffed_ptr package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_package) } -inline upb::reffed_ptr public_dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_public_dependency) } -inline upb::reffed_ptr service() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_service) } -inline upb::reffed_ptr source_code_info() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_source_code_info) } -inline upb::reffed_ptr syntax() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_syntax) } -inline upb::reffed_ptr weak_dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_weak_dependency) } -} /* namespace FileDescriptorProto */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace FileDescriptorSet { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileDescriptorSet) } -inline upb::reffed_ptr file() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorSet_file) } -} /* namespace FileDescriptorSet */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace FileOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileOptions) } -inline upb::reffed_ptr cc_enable_arenas() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_cc_enable_arenas) } -inline upb::reffed_ptr cc_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_cc_generic_services) } -inline upb::reffed_ptr csharp_namespace() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_csharp_namespace) } -inline upb::reffed_ptr deprecated() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_deprecated) } -inline upb::reffed_ptr go_package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_go_package) } -inline upb::reffed_ptr java_generate_equals_and_hash() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_generate_equals_and_hash) } -inline upb::reffed_ptr java_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_generic_services) } -inline upb::reffed_ptr java_multiple_files() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_multiple_files) } -inline upb::reffed_ptr java_outer_classname() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_outer_classname) } -inline upb::reffed_ptr java_package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_package) } -inline upb::reffed_ptr java_string_check_utf8() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_string_check_utf8) } -inline upb::reffed_ptr javanano_use_deprecated_package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_javanano_use_deprecated_package) } -inline upb::reffed_ptr objc_class_prefix() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_objc_class_prefix) } -inline upb::reffed_ptr optimize_for() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_optimize_for) } -inline upb::reffed_ptr py_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_py_generic_services) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_uninterpreted_option) } -inline upb::reffed_ptr OptimizeMode() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FileOptions_OptimizeMode) } -} /* namespace FileOptions */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace MessageOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MessageOptions) } -inline upb::reffed_ptr deprecated() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_deprecated) } -inline upb::reffed_ptr map_entry() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_map_entry) } -inline upb::reffed_ptr message_set_wire_format() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_message_set_wire_format) } -inline upb::reffed_ptr no_standard_descriptor_accessor() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_no_standard_descriptor_accessor) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_uninterpreted_option) } -} /* namespace MessageOptions */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace MethodDescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MethodDescriptorProto) } -inline upb::reffed_ptr client_streaming() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_client_streaming) } -inline upb::reffed_ptr input_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_input_type) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_name) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_options) } -inline upb::reffed_ptr output_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_output_type) } -inline upb::reffed_ptr server_streaming() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_server_streaming) } -} /* namespace MethodDescriptorProto */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace MethodOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MethodOptions) } -inline upb::reffed_ptr deprecated() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodOptions_deprecated) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodOptions_uninterpreted_option) } -} /* namespace MethodOptions */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace OneofDescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_OneofDescriptorProto) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_OneofDescriptorProto_name) } -} /* namespace OneofDescriptorProto */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace ServiceDescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_ServiceDescriptorProto) } -inline upb::reffed_ptr method() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_method) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_name) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_options) } -} /* namespace ServiceDescriptorProto */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace ServiceOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_ServiceOptions) } -inline upb::reffed_ptr deprecated() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceOptions_deprecated) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceOptions_uninterpreted_option) } -} /* namespace ServiceOptions */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace SourceCodeInfo { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_SourceCodeInfo) } -inline upb::reffed_ptr location() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_location) } -} /* namespace SourceCodeInfo */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace SourceCodeInfo { -namespace Location { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_SourceCodeInfo_Location) } -inline upb::reffed_ptr leading_comments() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_leading_comments) } -inline upb::reffed_ptr leading_detached_comments() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_leading_detached_comments) } -inline upb::reffed_ptr path() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_path) } -inline upb::reffed_ptr span() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_span) } -inline upb::reffed_ptr trailing_comments() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_trailing_comments) } -} /* namespace Location */ -} /* namespace SourceCodeInfo */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace UninterpretedOption { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_UninterpretedOption) } -inline upb::reffed_ptr aggregate_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_aggregate_value) } -inline upb::reffed_ptr double_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_double_value) } -inline upb::reffed_ptr identifier_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_identifier_value) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_name) } -inline upb::reffed_ptr negative_int_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_negative_int_value) } -inline upb::reffed_ptr positive_int_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_positive_int_value) } -inline upb::reffed_ptr string_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_string_value) } -} /* namespace UninterpretedOption */ -} /* namespace protobuf */ -} /* namespace google */ - -namespace google { -namespace protobuf { -namespace UninterpretedOption { -namespace NamePart { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_UninterpretedOption_NamePart) } -inline upb::reffed_ptr is_extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_NamePart_is_extension) } -inline upb::reffed_ptr name_part() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_NamePart_name_part) } -} /* namespace NamePart */ -} /* namespace UninterpretedOption */ -} /* namespace protobuf */ -} /* namespace google */ - } /* namespace upbdefs */ +#endif /* __cplusplus */ -#undef RETURN_REFFED -#endif /* __cplusplus */ - -#endif /* GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ */ +#endif /* UPB_DESCRIPTOR_DESCRIPTOR_PROTO_UPB_H_ */ /* ** Internal-only definitions for the decoder. */ @@ -7242,7 +7327,6 @@ inline upb::reffed_ptr name_part() { RETURN_REFFED(upb::Fie #ifndef UPB_DECODER_INT_H_ #define UPB_DECODER_INT_H_ -#include /* ** upb::pb::Decoder ** @@ -7342,7 +7426,7 @@ class upb::pb::DecoderMethod { * constructed. This hint may be an overestimate for some build configurations. * But if the decoder library is upgraded without recompiling the application, * it may be an underestimate. */ -#define UPB_PB_DECODER_SIZE 4408 +#define UPB_PB_DECODER_SIZE 4416 #ifdef __cplusplus @@ -8155,49 +8239,44 @@ inline reffed_ptr Encoder::NewHandlers( #include #ifdef __cplusplus +#include + extern "C" { #endif -/* Loads all defs from the given protobuf binary descriptor, setting default - * accessors and a default layout on all messages. The caller owns the - * returned array of defs, which will be of length *n. On error NULL is - * returned and status is set (if non-NULL). */ -upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n, - void *owner, upb_status *status); - -/* Like the previous but also adds the loaded defs to the given symtab. */ -bool upb_load_descriptor_into_symtab(upb_symtab *symtab, const char *str, - size_t len, upb_status *status); - -/* Like the previous but also reads the descriptor from the given filename. */ -bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname, - upb_status *status); - -/* Reads the given filename into a character string, returning NULL if there - * was an error. */ -char *upb_readfile(const char *filename, size_t *len); +/* Loads a binary descriptor and returns a NULL-terminated array of unfrozen + * filedefs. The caller owns the returned array, which must be freed with + * upb_gfree(). */ +upb_filedef **upb_loaddescriptor(const char *buf, size_t n, const void *owner, + upb_status *status); #ifdef __cplusplus } /* extern "C" */ namespace upb { -/* All routines that load descriptors expect the descriptor to be a - * FileDescriptorSet. */ -inline bool LoadDescriptorFileIntoSymtab(SymbolTable* s, const char *fname, - Status* status) { - return upb_load_descriptor_file_into_symtab(s, fname, status); -} +inline bool LoadDescriptor(const char* buf, size_t n, Status* status, + std::vector >* files) { + FileDef** parsed_files = upb_loaddescriptor(buf, n, &parsed_files, status); -inline bool LoadDescriptorIntoSymtab(SymbolTable* s, const char* str, - size_t len, Status* status) { - return upb_load_descriptor_into_symtab(s, str, len, status); + if (parsed_files) { + FileDef** p = parsed_files; + while (*p) { + files->push_back(reffed_ptr(*p, &parsed_files)); + ++p; + } + free(parsed_files); + return true; + } else { + return false; + } } /* Templated so it can accept both string and std::string. */ template -bool LoadDescriptorIntoSymtab(SymbolTable* s, const T& desc, Status* status) { - return upb_load_descriptor_into_symtab(s, desc.c_str(), desc.size(), status); +bool LoadDescriptor(const T& desc, Status* status, + std::vector >* files) { + return LoadDescriptor(desc.c_str(), desc.size(), status, files); } } /* namespace upb */ @@ -8314,7 +8393,7 @@ UPB_DECLARE_DERIVED_TYPE(upb::json::ParserMethod, upb::RefCounted, * constructed. This hint may be an overestimate for some build configurations. * But if the parser library is upgraded without recompiling the application, * it may be an underestimate. */ -#define UPB_JSON_PARSER_SIZE 4104 +#define UPB_JSON_PARSER_SIZE 4112 #ifdef __cplusplus @@ -8427,7 +8506,7 @@ UPB_DECLARE_TYPE(upb::json::Printer, upb_json_printer) /* upb::json::Printer *********************************************************/ -#define UPB_JSON_PRINTER_SIZE 168 +#define UPB_JSON_PRINTER_SIZE 176 #ifdef __cplusplus @@ -8440,8 +8519,12 @@ class upb::json::Printer { /* The input to the printer. */ Sink* input(); - /* Returns handlers for printing according to the specified schema. */ - static reffed_ptr NewHandlers(const upb::MessageDef* md); + /* Returns handlers for printing according to the specified schema. + * If preserve_proto_fieldnames is true, the output JSON will use the + * original .proto field names (ie. {"my_field":3}) instead of using + * camelCased names, which is the default: (eg. {"myField":3}). */ + static reffed_ptr NewHandlers(const upb::MessageDef* md, + bool preserve_proto_fieldnames); static const size_t kSize = UPB_JSON_PRINTER_SIZE; @@ -8458,6 +8541,7 @@ upb_json_printer *upb_json_printer_create(upb_env *e, const upb_handlers *h, upb_bytessink *output); upb_sink *upb_json_printer_input(upb_json_printer *p); const upb_handlers *upb_json_printer_newhandlers(const upb_msgdef *md, + bool preserve_fieldnames, const void *owner); UPB_END_EXTERN_C @@ -8472,8 +8556,9 @@ inline Printer* Printer::Create(Environment* env, const upb::Handlers* handlers, } inline Sink* Printer::input() { return upb_json_printer_input(this); } inline reffed_ptr Printer::NewHandlers( - const upb::MessageDef *md) { - const Handlers* h = upb_json_printer_newhandlers(md, &h); + const upb::MessageDef *md, bool preserve_proto_fieldnames) { + const Handlers* h = upb_json_printer_newhandlers( + md, preserve_proto_fieldnames, &h); return reffed_ptr(h, &h); } } /* namespace json */ diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index 7b64ee779..1fa626feb 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.0.0.alpha.5.0" + s.version = "3.0.0.alpha.5.0.5" s.licenses = ["BSD"] s.summary = "Protocol Buffers" s.description = "Protocol Buffers are Google's data interchange format." diff --git a/ruby/lib/google/protobuf/repeated_field.rb b/ruby/lib/google/protobuf/repeated_field.rb index 16c843c07..0ad2060d4 100644 --- a/ruby/lib/google/protobuf/repeated_field.rb +++ b/ruby/lib/google/protobuf/repeated_field.rb @@ -69,8 +69,8 @@ module Google # relationship explicit instead of implicit def_delegators :to_ary, :&, :*, :-, :'<=>', - :assoc, :bsearch, :combination, :compact, :count, :cycle, - :drop, :drop_while, :eql?, :fetch, :find_index, :flatten, + :assoc, :bsearch, :bsearch_index, :combination, :compact, :count, + :cycle, :dig, :drop, :drop_while, :eql?, :fetch, :find_index, :flatten, :include?, :index, :inspect, :join, :pack, :permutation, :product, :pretty_print, :pretty_print_cycle, :rassoc, :repeated_combination, :repeated_permutation, :reverse, diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index 86c74c2a0..77c186ef3 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -1161,7 +1161,12 @@ module BasicTest return if RUBY_PLATFORM == "java" m = MapMessage.new(:map_string_int32 => {"a" => 1}) expected = '{"mapStringInt32":{"a":1},"mapStringMsg":{}}' + expected_preserve = '{"map_string_int32":{"a":1},"map_string_msg":{}}' assert MapMessage.encode_json(m) == expected + + json = MapMessage.encode_json(m, :preserve_proto_fieldnames => true) + assert json == expected_preserve + m2 = MapMessage.decode_json(MapMessage.encode_json(m)) assert m == m2 end diff --git a/src/Makefile.am b/src/Makefile.am index 073673a5e..800655062 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -455,6 +455,7 @@ libprotoc_la_SOURCES = \ google/protobuf/compiler/csharp/csharp_message.h \ google/protobuf/compiler/csharp/csharp_message_field.cc \ google/protobuf/compiler/csharp/csharp_message_field.h \ + google/protobuf/compiler/csharp/csharp_options.h \ google/protobuf/compiler/csharp/csharp_primitive_field.cc \ google/protobuf/compiler/csharp/csharp_primitive_field.h \ google/protobuf/compiler/csharp/csharp_reflection_class.cc \ @@ -506,6 +507,8 @@ protoc_inputs = \ google/protobuf/unittest_preserve_unknown_enum.proto \ google/protobuf/unittest.proto \ google/protobuf/unittest_proto3_arena.proto \ + google/protobuf/unittest_proto3_arena_lite.proto \ + google/protobuf/unittest_proto3_lite.proto \ google/protobuf/unittest_well_known_types.proto \ google/protobuf/util/internal/testdata/anys.proto \ google/protobuf/util/internal/testdata/books.proto \ @@ -608,6 +611,10 @@ protoc_outputs = \ google/protobuf/unittest_preserve_unknown_enum.pb.h \ google/protobuf/unittest_proto3_arena.pb.cc \ google/protobuf/unittest_proto3_arena.pb.h \ + google/protobuf/unittest_proto3_arena_lite.pb.cc \ + google/protobuf/unittest_proto3_arena_lite.pb.h \ + google/protobuf/unittest_proto3_lite.pb.cc \ + google/protobuf/unittest_proto3_lite.pb.h \ google/protobuf/unittest_well_known_types.pb.cc \ google/protobuf/unittest_well_known_types.pb.h \ google/protobuf/util/internal/testdata/anys.pb.cc \ @@ -709,6 +716,8 @@ protobuf_test_SOURCES = \ google/protobuf/no_field_presence_test.cc \ google/protobuf/preserve_unknown_enum_test.cc \ google/protobuf/proto3_arena_unittest.cc \ + google/protobuf/proto3_arena_lite_unittest.cc \ + google/protobuf/proto3_lite_unittest.cc \ google/protobuf/reflection_ops_unittest.cc \ google/protobuf/repeated_field_reflection_unittest.cc \ google/protobuf/repeated_field_unittest.cc \ diff --git a/src/README.md b/src/README.md index 4e312c0c1..63d6e24cc 100644 --- a/src/README.md +++ b/src/README.md @@ -16,10 +16,13 @@ To build protobuf from source, the following tools are needed: * automake * libtool * curl (used to download gmock) + * make + * g++ + * unzip On Ubuntu, you can install them with: - $ sudo apt-get install autoconf automake libtool curl + $ sudo apt-get install autoconf automake libtool curl make g++ unzip On other platforms, please use the corresponding package managing tool to install them before proceeding. @@ -181,7 +184,7 @@ In the downloads section, download the zip file protoc-$VERSION-win32.zip. It contains the protoc binary as well as public proto files of protobuf library. -To build from source using Microsoft Visual C++, see cmake/README.md. +To build from source using Microsoft Visual C++, see [cmake/README.md](../cmake/README.md). To build from source using Cygwin or MinGW, follow the Unix installation instructions, above. diff --git a/src/google/protobuf/any.cc b/src/google/protobuf/any.cc index f3ca06bfc..f7b1d3106 100644 --- a/src/google/protobuf/any.cc +++ b/src/google/protobuf/any.cc @@ -35,10 +35,15 @@ namespace protobuf { namespace internal { namespace { -string GetTypeUrl(const Descriptor* message) { - return string(kTypeGoogleApisComPrefix) + message->full_name(); +string GetTypeUrl(const Descriptor* message, + const string& type_url_prefix) { + if (!type_url_prefix.empty() && + type_url_prefix[type_url_prefix.size() - 1] == '/') { + return type_url_prefix + message->full_name(); + } else { + return type_url_prefix + "/" + message->full_name(); + } } - } // namespace const char kAnyFullTypeName[] = "google.protobuf.Any"; @@ -50,8 +55,13 @@ AnyMetadata::AnyMetadata(UrlType* type_url, ValueType* value) } void AnyMetadata::PackFrom(const Message& message) { + PackFrom(message, kTypeGoogleApisComPrefix); +} + +void AnyMetadata::PackFrom(const Message& message, + const string& type_url_prefix) { type_url_->SetNoArena(&::google::protobuf::internal::GetEmptyString(), - GetTypeUrl(message.GetDescriptor())); + GetTypeUrl(message.GetDescriptor(), type_url_prefix)); message.SerializeToString(value_->MutableNoArena( &::google::protobuf::internal::GetEmptyStringAlreadyInited())); } @@ -67,30 +77,20 @@ bool AnyMetadata::UnpackTo(Message* message) const { bool AnyMetadata::InternalIs(const Descriptor* descriptor) const { const string type_url = type_url_->GetNoArena( &::google::protobuf::internal::GetEmptyString()); - const string full_name = descriptor->full_name(); - if (type_url.length() < full_name.length()) { - return false; + string full_name; + if (!ParseAnyTypeUrl(type_url, &full_name)) { + return false; } - return (0 == type_url.compare( - type_url.length() - full_name.length(), - full_name.length(), - full_name)); + return full_name == descriptor->full_name(); } bool ParseAnyTypeUrl(const string& type_url, string* full_type_name) { - static const char* prefix[] = { - kTypeGoogleApisComPrefix, - kTypeGoogleProdComPrefix - }; - for (int i = 0; i < 2; i++) { - const int prefix_len = strlen(prefix[i]); - if (strncmp(type_url.c_str(), prefix[i], prefix_len) == 0) { - full_type_name->assign(type_url.data() + prefix_len, - type_url.size() - prefix_len); - return true; - } + size_t pos = type_url.find_last_of("/"); + if (pos == string::npos || pos + 1 == type_url.size()) { + return false; } - return false; + *full_type_name = type_url.substr(pos + 1); + return true; } diff --git a/src/google/protobuf/any.h b/src/google/protobuf/any.h index c8dbef130..04e541667 100644 --- a/src/google/protobuf/any.h +++ b/src/google/protobuf/any.h @@ -50,10 +50,26 @@ class LIBPROTOBUF_EXPORT AnyMetadata { // AnyMetadata does not take ownership of "type_url" and "value". AnyMetadata(UrlType* type_url, ValueType* value); + // Packs a message using the default type URL prefix: "type.googleapis.com". + // The resulted type URL will be "type.googleapis.com/". void PackFrom(const Message& message); + // Packs a message using the given type URL prefix. The type URL will be + // constructed by concatenating the message type's full name to the prefix + // with an optional "/" separator if the prefix doesn't already end up "/". + // For example, both PackFrom(message, "type.googleapis.com") and + // PackFrom(message, "type.googleapis.com/") yield the same result type + // URL: "type.googleapis.com/". + void PackFrom(const Message& message, const string& type_url_prefix); + // Unpacks the payload into the given message. Returns false if the message's + // type doesn't match the type specified in the type URL (i.e., the full + // name after the last "/" of the type URL doesn't match the message's actaul + // full name) or parsing the payload has failed. bool UnpackTo(Message* message) const; + // Checks whether the type specified in the type URL matches the given type. + // A type is consdiered matching if its full name matches the full name after + // the last "/" in the type URL. template bool Is() const { return InternalIs(T::default_instance().GetDescriptor()); diff --git a/src/google/protobuf/any.pb.cc b/src/google/protobuf/any.pb.cc index 0bf523b37..fbde4a6d7 100644 --- a/src/google/protobuf/any.pb.cc +++ b/src/google/protobuf/any.pb.cc @@ -83,9 +83,10 @@ void protobuf_AddDesc_google_2fprotobuf_2fany_2eproto() { ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( "\n\031google/protobuf/any.proto\022\017google.prot" "obuf\"&\n\003Any\022\020\n\010type_url\030\001 \001(\t\022\r\n\005value\030\002" - " \001(\014BK\n\023com.google.protobufB\010AnyProtoP\001\240" - "\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownType" - "sb\006proto3", 169); + " \001(\014Br\n\023com.google.protobufB\010AnyProtoP\001Z" + "%github.com/golang/protobuf/ptypes/any\240\001" + "\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes" + "b\006proto3", 208); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/any.proto", &protobuf_RegisterTypes); Any::default_instance_ = new Any(); @@ -116,6 +117,11 @@ void Any::PackFrom(const ::google::protobuf::Message& message) { _any_metadata_.PackFrom(message); } +void Any::PackFrom(const ::google::protobuf::Message& message, + const ::std::string& type_url_prefix) { + _any_metadata_.PackFrom(message, type_url_prefix); +} + bool Any::UnpackTo(::google::protobuf::Message* message) const { return _any_metadata_.UnpackTo(message); } @@ -190,13 +196,14 @@ Any* Any::New(::google::protobuf::Arena* arena) const { } void Any::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Any) type_url_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } bool Any::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Any) for (;;) { @@ -302,6 +309,7 @@ void Any::SerializeWithCachedSizes( } int Any::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Any) int total_size = 0; // optional string type_url = 1; @@ -325,18 +333,22 @@ int Any::ByteSize() const { } void Any::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Any) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Any* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Any) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Any) MergeFrom(*source); } } void Any::MergeFrom(const Any& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Any) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.type_url().size() > 0) { @@ -349,12 +361,14 @@ void Any::MergeFrom(const Any& from) { } void Any::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Any) if (&from == this) return; Clear(); MergeFrom(from); } void Any::CopyFrom(const Any& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Any) if (&from == this) return; Clear(); MergeFrom(from); @@ -417,6 +431,7 @@ void Any::clear_type_url() { return type_url_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Any::release_type_url() { + // @@protoc_insertion_point(field_release:google.protobuf.Any.type_url) return type_url_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -460,6 +475,7 @@ void Any::clear_value() { return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Any::release_value() { + // @@protoc_insertion_point(field_release:google.protobuf.Any.value) return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } diff --git a/src/google/protobuf/any.pb.h b/src/google/protobuf/any.pb.h index 97982ecf8..100a67f61 100644 --- a/src/google/protobuf/any.pb.h +++ b/src/google/protobuf/any.pb.h @@ -60,6 +60,8 @@ class LIBPROTOBUF_EXPORT Any : public ::google::protobuf::Message { // implements Any ----------------------------------------------- void PackFrom(const ::google::protobuf::Message& message); + void PackFrom(const ::google::protobuf::Message& message, + const ::std::string& type_url_prefix); bool UnpackTo(::google::protobuf::Message* message) const; template bool Is() const { return _any_metadata_.Is(); @@ -182,6 +184,7 @@ inline ::std::string* Any::mutable_type_url() { return type_url_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Any::release_type_url() { + // @@protoc_insertion_point(field_release:google.protobuf.Any.type_url) return type_url_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -225,6 +228,7 @@ inline ::std::string* Any::mutable_value() { return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Any::release_value() { + // @@protoc_insertion_point(field_release:google.protobuf.Any.value) return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } diff --git a/src/google/protobuf/any.proto b/src/google/protobuf/any.proto index e8a18bc35..45db6ede3 100644 --- a/src/google/protobuf/any.proto +++ b/src/google/protobuf/any.proto @@ -33,14 +33,43 @@ syntax = "proto3"; package google.protobuf; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/any"; option java_package = "com.google.protobuf"; option java_outer_classname = "AnyProto"; option java_multiple_files = true; option java_generate_equals_and_hash = true; option objc_class_prefix = "GPB"; -// `Any` contains an arbitrary serialized message along with a URL -// that describes the type of the serialized message. +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". // // // JSON @@ -73,7 +102,7 @@ option objc_class_prefix = "GPB"; // message Any { // A URL/resource name whose content describes the type of the - // serialized message. + // serialized protocol buffer message. // // For URLs which use the schema `http`, `https`, or no schema, the // following restrictions and interpretations apply: @@ -81,6 +110,8 @@ message Any { // * If no schema is provided, `https` is assumed. // * The last segment of the URL's path must represent the fully // qualified name of the type (as in `path/google.protobuf.Duration`). + // The name should be in a canonical form (e.g., leading "." is + // not accepted). // * An HTTP GET on the URL must yield a [google.protobuf.Type][] // value in binary format, or produce an error. // * Applications are allowed to cache lookup results based on the @@ -94,6 +125,6 @@ message Any { // string type_url = 1; - // Must be valid serialized data of the above specified type. + // Must be a valid serialized protocol buffer of the above specified type. bytes value = 2; } diff --git a/src/google/protobuf/api.pb.cc b/src/google/protobuf/api.pb.cc index e589a89dd..cbeba3023 100644 --- a/src/google/protobuf/api.pb.cc +++ b/src/google/protobuf/api.pb.cc @@ -265,6 +265,7 @@ Api* Api::New(::google::protobuf::Arena* arena) const { } void Api::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Api) name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); version_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; @@ -277,7 +278,7 @@ void Api::Clear() { bool Api::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Api) for (;;) { @@ -538,6 +539,7 @@ void Api::SerializeWithCachedSizes( } int Api::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Api) int total_size = 0; // optional string name = 1; @@ -598,18 +600,22 @@ int Api::ByteSize() const { } void Api::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Api) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Api* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Api) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Api) MergeFrom(*source); } } void Api::MergeFrom(const Api& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Api) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); methods_.MergeFrom(from.methods_); options_.MergeFrom(from.options_); @@ -631,12 +637,14 @@ void Api::MergeFrom(const Api& from) { } void Api::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Api) if (&from == this) return; Clear(); MergeFrom(from); } void Api::CopyFrom(const Api& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Api) if (&from == this) return; Clear(); MergeFrom(from); @@ -704,6 +712,7 @@ void Api::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Api::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Api.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -807,6 +816,7 @@ void Api::clear_version() { return version_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Api::release_version() { + // @@protoc_insertion_point(field_release:google.protobuf.Api.version) return version_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -841,6 +851,7 @@ const ::google::protobuf::SourceContext& Api::source_context() const { return source_context_; } ::google::protobuf::SourceContext* Api::release_source_context() { + // @@protoc_insertion_point(field_release:google.protobuf.Api.source_context) ::google::protobuf::SourceContext* temp = source_context_; source_context_ = NULL; @@ -984,8 +995,17 @@ Method* Method::New(::google::protobuf::Arena* arena) const { } void Method::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Method) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(Method, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -1005,7 +1025,7 @@ void Method::Clear() { bool Method::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Method) for (;;) { @@ -1269,6 +1289,7 @@ void Method::SerializeWithCachedSizes( } int Method::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Method) int total_size = 0; // optional string name = 1; @@ -1323,18 +1344,22 @@ int Method::ByteSize() const { } void Method::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Method) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Method* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Method) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Method) MergeFrom(*source); } } void Method::MergeFrom(const Method& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Method) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); options_.MergeFrom(from.options_); if (from.name().size() > 0) { @@ -1361,12 +1386,14 @@ void Method::MergeFrom(const Method& from) { } void Method::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Method) if (&from == this) return; Clear(); MergeFrom(from); } void Method::CopyFrom(const Method& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Method) if (&from == this) return; Clear(); MergeFrom(from); @@ -1434,6 +1461,7 @@ void Method::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Method::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Method.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1477,6 +1505,7 @@ void Method::clear_request_type_url() { return request_type_url_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Method::release_request_type_url() { + // @@protoc_insertion_point(field_release:google.protobuf.Method.request_type_url) return request_type_url_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1534,6 +1563,7 @@ void Method::clear_response_type_url() { return response_type_url_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Method::release_response_type_url() { + // @@protoc_insertion_point(field_release:google.protobuf.Method.response_type_url) return response_type_url_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1678,13 +1708,14 @@ Mixin* Mixin::New(::google::protobuf::Arena* arena) const { } void Mixin::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Mixin) name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); root_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } bool Mixin::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Mixin) for (;;) { @@ -1802,6 +1833,7 @@ void Mixin::SerializeWithCachedSizes( } int Mixin::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Mixin) int total_size = 0; // optional string name = 1; @@ -1825,18 +1857,22 @@ int Mixin::ByteSize() const { } void Mixin::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Mixin) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Mixin* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Mixin) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Mixin) MergeFrom(*source); } } void Mixin::MergeFrom(const Mixin& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Mixin) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.name().size() > 0) { @@ -1849,12 +1885,14 @@ void Mixin::MergeFrom(const Mixin& from) { } void Mixin::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Mixin) if (&from == this) return; Clear(); MergeFrom(from); } void Mixin::CopyFrom(const Mixin& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Mixin) if (&from == this) return; Clear(); MergeFrom(from); @@ -1917,6 +1955,7 @@ void Mixin::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Mixin::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Mixin.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1960,6 +1999,7 @@ void Mixin::clear_root() { return root_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Mixin::release_root() { + // @@protoc_insertion_point(field_release:google.protobuf.Mixin.root) return root_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } diff --git a/src/google/protobuf/api.pb.h b/src/google/protobuf/api.pb.h index e1dca4e49..bb35e471a 100644 --- a/src/google/protobuf/api.pb.h +++ b/src/google/protobuf/api.pb.h @@ -468,6 +468,7 @@ inline ::std::string* Api::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Api::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Api.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -571,6 +572,7 @@ inline ::std::string* Api::mutable_version() { return version_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Api::release_version() { + // @@protoc_insertion_point(field_release:google.protobuf.Api.version) return version_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -605,6 +607,7 @@ inline ::google::protobuf::SourceContext* Api::mutable_source_context() { return source_context_; } inline ::google::protobuf::SourceContext* Api::release_source_context() { + // @@protoc_insertion_point(field_release:google.protobuf.Api.source_context) ::google::protobuf::SourceContext* temp = source_context_; source_context_ = NULL; @@ -699,6 +702,7 @@ inline ::std::string* Method::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Method::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Method.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -742,6 +746,7 @@ inline ::std::string* Method::mutable_request_type_url() { return request_type_url_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Method::release_request_type_url() { + // @@protoc_insertion_point(field_release:google.protobuf.Method.request_type_url) return request_type_url_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -799,6 +804,7 @@ inline ::std::string* Method::mutable_response_type_url() { return response_type_url_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Method::release_response_type_url() { + // @@protoc_insertion_point(field_release:google.protobuf.Method.response_type_url) return response_type_url_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -904,6 +910,7 @@ inline ::std::string* Mixin::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Mixin::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Mixin.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -947,6 +954,7 @@ inline ::std::string* Mixin::mutable_root() { return root_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Mixin::release_root() { + // @@protoc_insertion_point(field_release:google.protobuf.Mixin.root) return root_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } diff --git a/src/google/protobuf/arena.cc b/src/google/protobuf/arena.cc index cd0b21a7f..613e58971 100755 --- a/src/google/protobuf/arena.cc +++ b/src/google/protobuf/arena.cc @@ -30,6 +30,7 @@ #include + #ifdef ADDRESS_SANITIZER #include #endif @@ -37,6 +38,7 @@ namespace google { namespace protobuf { + google::protobuf::internal::SequenceNumber Arena::lifecycle_id_generator_; #if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL) Arena::ThreadCache& Arena::thread_cache() { @@ -247,6 +249,19 @@ uint64 Arena::SpaceUsed() const { return space_used; } +pair Arena::SpaceAllocatedAndUsed() const { + uint64 allocated = 0; + uint64 used = 0; + + Block* b = reinterpret_cast(google::protobuf::internal::NoBarrier_Load(&blocks_)); + while (b != NULL) { + allocated += b->size; + used += (b->pos - kHeaderSize); + b = b->next; + } + return std::make_pair(allocated, used); +} + uint64 Arena::FreeBlocks() { uint64 space_allocated = 0; Block* b = reinterpret_cast(google::protobuf::internal::NoBarrier_Load(&blocks_)); diff --git a/src/google/protobuf/arena.h b/src/google/protobuf/arena.h index 5ad94fa99..b3d66d91d 100644 --- a/src/google/protobuf/arena.h +++ b/src/google/protobuf/arena.h @@ -56,6 +56,7 @@ using type_info = ::type_info; #include #include + namespace google { namespace protobuf { @@ -210,7 +211,12 @@ struct ArenaOptions { // // This protocol is implemented by all arena-enabled proto2 message classes as // well as RepeatedPtrField. + +#if __cplusplus >= 201103L +class Arena final { +#else class LIBPROTOBUF_EXPORT Arena { +#endif public: // Arena constructor taking custom options. See ArenaOptions below for // descriptions of the options available. @@ -446,6 +452,10 @@ class LIBPROTOBUF_EXPORT Arena { // As above, but does not include any free space in underlying blocks. GOOGLE_ATTRIBUTE_NOINLINE uint64 SpaceUsed() const; + // Combines SpaceAllocated and SpaceUsed. Returns a pair of + // . + GOOGLE_ATTRIBUTE_NOINLINE pair SpaceAllocatedAndUsed() const; + // Frees all storage allocated by this arena after calling destructors // registered with OwnDestructor() and freeing objects registered with Own(). // Any objects allocated on this arena are unusable after this call. It also @@ -507,11 +517,11 @@ class LIBPROTOBUF_EXPORT Arena { // // This is inside Arena because only Arena has the friend relationships // necessary to see the underlying generated code traits. - template - struct is_arena_constructable : - public google::protobuf::internal::integral_constant(static_cast(0))) == sizeof(char)> { + template + struct is_arena_constructable + : public google::protobuf::internal::integral_constant< + bool, sizeof(InternalIsArenaConstructableHelper::ArenaConstructable< + const T>(static_cast(0))) == sizeof(char)> { }; private: diff --git a/src/google/protobuf/arena_nc_test.py b/src/google/protobuf/arena_nc_test.py index 87a69b2af..56a7dd05a 100644 --- a/src/google/protobuf/arena_nc_test.py +++ b/src/google/protobuf/arena_nc_test.py @@ -35,7 +35,7 @@ import unittest from google3.testing.pybase import fake_target_util -import unittest +from google3.testing.pybase import unittest class ArenaNcTest(unittest.TestCase): diff --git a/src/google/protobuf/arena_unittest.cc b/src/google/protobuf/arena_unittest.cc index 6b67f446f..ab25ffe1a 100644 --- a/src/google/protobuf/arena_unittest.cc +++ b/src/google/protobuf/arena_unittest.cc @@ -342,6 +342,64 @@ TEST(ArenaTest, Swap) { EXPECT_EQ(42, arena2_message->unknown_fields().field(0).varint()); } +TEST(ArenaTest, ReflectionSwapFields) { + Arena arena1; + Arena arena2; + TestAllTypes* arena1_message; + TestAllTypes* arena2_message; + + // Case 1: messages on different arenas, only one message is set. + arena1_message = Arena::CreateMessage(&arena1); + arena2_message = Arena::CreateMessage(&arena2); + TestUtil::SetAllFields(arena1_message); + const Reflection* reflection = arena1_message->GetReflection(); + std::vector fields; + reflection->ListFields(*arena1_message, &fields); + reflection->SwapFields(arena1_message, arena2_message, fields); + EXPECT_EQ(&arena1, arena1_message->GetArena()); + EXPECT_EQ(&arena2, arena2_message->GetArena()); + string output; + arena1_message->SerializeToString(&output); + EXPECT_EQ(0, output.size()); + TestUtil::ExpectAllFieldsSet(*arena2_message); + reflection->SwapFields(arena1_message, arena2_message, fields); + arena2_message->SerializeToString(&output); + EXPECT_EQ(0, output.size()); + TestUtil::ExpectAllFieldsSet(*arena1_message); + + // Case 2: messages on different arenas, both messages are set. + arena1_message = Arena::CreateMessage(&arena1); + arena2_message = Arena::CreateMessage(&arena2); + TestUtil::SetAllFields(arena1_message); + TestUtil::SetAllFields(arena2_message); + reflection->SwapFields(arena1_message, arena2_message, fields); + EXPECT_EQ(&arena1, arena1_message->GetArena()); + EXPECT_EQ(&arena2, arena2_message->GetArena()); + TestUtil::ExpectAllFieldsSet(*arena1_message); + TestUtil::ExpectAllFieldsSet(*arena2_message); + + // Case 3: messages on different arenas with different lifetimes. + arena1_message = Arena::CreateMessage(&arena1); + { + Arena arena3; + TestAllTypes* arena3_message = Arena::CreateMessage(&arena3); + TestUtil::SetAllFields(arena3_message); + reflection->SwapFields(arena1_message, arena3_message, fields); + } + TestUtil::ExpectAllFieldsSet(*arena1_message); + + // Case 4: one message on arena, the other on heap. + arena1_message = Arena::CreateMessage(&arena1); + TestAllTypes message; + TestUtil::SetAllFields(arena1_message); + reflection->SwapFields(arena1_message, &message, fields); + EXPECT_EQ(&arena1, arena1_message->GetArena()); + EXPECT_EQ(NULL, message.GetArena()); + arena1_message->SerializeToString(&output); + EXPECT_EQ(0, output.size()); + TestUtil::ExpectAllFieldsSet(message); +} + TEST(ArenaTest, SetAllocatedMessage) { Arena arena; TestAllTypes *arena_message = Arena::CreateMessage(&arena); diff --git a/src/google/protobuf/arenastring.h b/src/google/protobuf/arenastring.h index e2e2f2546..590ffce9a 100755 --- a/src/google/protobuf/arenastring.h +++ b/src/google/protobuf/arenastring.h @@ -134,7 +134,8 @@ struct LIBPROTOBUF_EXPORT ArenaStringPtr { // UnsafeArenaRelease() on another field of a message in the same arena. Used // to implement unsafe_arena_set_allocated_ in generated classes. inline void UnsafeArenaSetAllocated(const ::std::string* default_value, - ::std::string* value, ::google::protobuf::Arena* /* arena */) { + ::std::string* value, + ::google::protobuf::Arena* /* arena */) { if (value != NULL) { ptr_ = value; } else { diff --git a/src/google/protobuf/arenastring_unittest.cc b/src/google/protobuf/arenastring_unittest.cc index 3fb582be1..ea405d7d3 100644 --- a/src/google/protobuf/arenastring_unittest.cc +++ b/src/google/protobuf/arenastring_unittest.cc @@ -28,9 +28,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// arenastring_unittest.cc is not open-sourced. Do not include in open-source -// distribution. - // Based on mvels@'s frankenstring. #include diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index 3a816b056..fcad6b616 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -46,10 +46,11 @@ #include #endif #include +#include #include #include -#ifdef GOOGLE_PROTOBUF_ARCH_SPARC +#ifdef GOOGLE_PROTOBUF_ARCH_SPARC #include //For PATH_MAX #endif @@ -948,17 +949,23 @@ bool CommandLineInterface::MakeInputsBeProtoPathRelative( return true; } + CommandLineInterface::ParseArgumentStatus CommandLineInterface::ParseArguments(int argc, const char* const argv[]) { executable_name_ = argv[0]; + vector arguments; + for (int i = 1; i < argc; ++i) { + arguments.push_back(argv[i]); + } + // Iterate through all arguments and parse them. - for (int i = 1; i < argc; i++) { + for (int i = 0; i < arguments.size(); ++i) { string name, value; - if (ParseArgument(argv[i], &name, &value)) { + if (ParseArgument(arguments[i].c_str(), &name, &value)) { // Returned true => Use the next argument as the flag value. - if (i + 1 == argc || argv[i+1][0] == '-') { + if (i + 1 == arguments.size() || arguments[i + 1][0] == '-') { std::cerr << "Missing value for flag: " << name << std::endl; if (name == "--decode") { std::cerr << "To decode an unknown message, use --decode_raw." @@ -967,7 +974,7 @@ CommandLineInterface::ParseArguments(int argc, const char* const argv[]) { return PARSE_ARGUMENT_FAIL; } else { ++i; - value = argv[i]; + value = arguments[i]; } } diff --git a/src/google/protobuf/compiler/command_line_interface.h b/src/google/protobuf/compiler/command_line_interface.h index f196ffc57..d1377666d 100644 --- a/src/google/protobuf/compiler/command_line_interface.h +++ b/src/google/protobuf/compiler/command_line_interface.h @@ -212,6 +212,7 @@ class LIBPROTOC_EXPORT CommandLineInterface { // Parse all command-line arguments. ParseArgumentStatus ParseArguments(int argc, const char* const argv[]); + // Parses a command-line argument into a name/value pair. Returns // true if the next argument in the argv should be used as the value, // false otherwise. diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc index dda007d48..ae2900b1a 100644 --- a/src/google/protobuf/compiler/command_line_interface_unittest.cc +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -60,16 +60,17 @@ #include #include +#include #include #include -namespace google { -namespace protobuf { -namespace compiler { // Disable the whole test when we use tcmalloc for "draconian" heap checks, in // which case tcmalloc will print warnings that fail the plugin tests. #if !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN +namespace google { +namespace protobuf { +namespace compiler { #if defined(_WIN32) #ifndef STDIN_FILENO @@ -376,7 +377,9 @@ void CommandLineInterfaceTest::CreateTempFile( // Write file. string full_name = temp_directory_ + "/" + name; - GOOGLE_CHECK_OK(File::SetContents(full_name, contents, true)); + GOOGLE_CHECK_OK(File::SetContents( + full_name, StringReplace(contents, "$tmpdir", temp_directory_, true), + true)); } void CommandLineInterfaceTest::CreateTempDir(const string& name) { @@ -1090,6 +1093,7 @@ TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileForAbsolutePath) { } #endif // !_WIN32 + // ------------------------------------------------------------------- TEST_F(CommandLineInterfaceTest, ParseErrors) { diff --git a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc index 47729e1cb..77451ab1b 100644 --- a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc @@ -53,6 +53,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.cc b/src/google/protobuf/compiler/cpp/cpp_enum.cc index 5ee6f0003..c81c59821 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum.cc @@ -69,11 +69,12 @@ EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, EnumGenerator::~EnumGenerator() {} -void EnumGenerator::FillForwardDeclaration(set* enum_names) { +void EnumGenerator::FillForwardDeclaration( + map* enum_names) { if (!options_.proto_h) { return; } - enum_names->insert(classname_); + (*enum_names)[classname_] = descriptor_; } void EnumGenerator::GenerateDefinition(io::Printer* printer) { @@ -83,6 +84,7 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) { vars["enumbase"] = classname_ + (options_.proto_h ? " : int" : ""); printer->Print(vars, "enum $enumbase$ {\n"); + printer->Annotate("enumbase", descriptor_); printer->Indent(); const EnumValueDescriptor* min_value = descriptor_->value(0); @@ -96,9 +98,11 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) { vars["number"] = Int32ToString(descriptor_->value(i)->number()); vars["prefix"] = (descriptor_->containing_type() == NULL) ? "" : classname_ + "_"; + vars["deprecation"] = descriptor_->value(i)->options().deprecated() ? + " PROTOBUF_DEPRECATED" : ""; if (i > 0) printer->Print(",\n"); - printer->Print(vars, "$prefix$$name$ = $number$"); + printer->Print(vars, "$prefix$$name$$deprecation$ = $number$"); if (descriptor_->value(i)->number() < min_value->number()) { min_value = descriptor_->value(i); @@ -140,15 +144,16 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) { "$prefix$$short_name$_MAX + 1;\n\n"); } - if (HasDescriptorMethods(descriptor_->file())) { + if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print(vars, "$dllexport$const ::google::protobuf::EnumDescriptor* $classname$_descriptor();\n"); // The _Name and _Parse methods - printer->Print(vars, - "inline const ::std::string& $classname$_Name($classname$ value) {\n" - " return ::google::protobuf::internal::NameOfEnum(\n" - " $classname$_descriptor(), value);\n" - "}\n"); + printer->Print( + vars, + "inline const ::std::string& $classname$_Name($classname$ value) {\n" + " return ::google::protobuf::internal::NameOfEnum(\n" + " $classname$_descriptor(), value);\n" + "}\n"); printer->Print(vars, "inline bool $classname$_Parse(\n" " const ::std::string& name, $classname$* value) {\n" @@ -164,7 +169,7 @@ GenerateGetEnumDescriptorSpecializations(io::Printer* printer) { "template <> struct is_proto_enum< $classname$> : ::google::protobuf::internal::true_type " "{};\n", "classname", ClassName(descriptor_, true)); - if (HasDescriptorMethods(descriptor_->file())) { + if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print( "template <>\n" "inline const EnumDescriptor* GetEnumDescriptor< $classname$>() {\n" @@ -183,8 +188,11 @@ void EnumGenerator::GenerateSymbolImports(io::Printer* printer) { for (int j = 0; j < descriptor_->value_count(); j++) { vars["tag"] = EnumValueName(descriptor_->value(j)); + vars["deprecated_attr"] = descriptor_->value(j)->options().deprecated() ? + "PROTOBUF_DEPRECATED_ATTR " : ""; printer->Print(vars, - "static $constexpr$const $nested_name$ $tag$ = $classname$_$tag$;\n"); + "$deprecated_attr$static $constexpr$const $nested_name$ $tag$ =\n" + " $classname$_$tag$;\n"); } printer->Print(vars, @@ -201,16 +209,18 @@ void EnumGenerator::GenerateSymbolImports(io::Printer* printer) { " $classname$_$nested_name$_ARRAYSIZE;\n"); } - if (HasDescriptorMethods(descriptor_->file())) { + if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print(vars, "static inline const ::google::protobuf::EnumDescriptor*\n" "$nested_name$_descriptor() {\n" " return $classname$_descriptor();\n" "}\n"); printer->Print(vars, - "static inline const ::std::string& $nested_name$_Name($nested_name$ value) {\n" - " return $classname$_Name(value);\n" - "}\n"); + "static inline const ::std::string& " + "$nested_name$_Name($nested_name$ value) {" + "\n" + " return $classname$_Name(value);\n" + "}\n"); printer->Print(vars, "static inline bool $nested_name$_Parse(const ::std::string& name,\n" " $nested_name$* value) {\n" @@ -240,7 +250,7 @@ void EnumGenerator::GenerateMethods(io::Printer* printer) { vars["classname"] = classname_; vars["constexpr"] = options_.proto_h ? "constexpr " : ""; - if (HasDescriptorMethods(descriptor_->file())) { + if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print(vars, "const ::google::protobuf::EnumDescriptor* $classname$_descriptor() {\n" " protobuf_AssignDescriptorsOnce();\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.h b/src/google/protobuf/compiler/cpp/cpp_enum.h index f3aa72e44..90edf0017 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.h +++ b/src/google/protobuf/compiler/cpp/cpp_enum.h @@ -35,12 +35,12 @@ #ifndef GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__ #define GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__ +#include #include #include #include #include - namespace google { namespace protobuf { namespace io { @@ -55,8 +55,7 @@ namespace cpp { class EnumGenerator { public: // See generator.cc for the meaning of dllexport_decl. - explicit EnumGenerator(const EnumDescriptor* descriptor, - const Options& options); + EnumGenerator(const EnumDescriptor* descriptor, const Options& options); ~EnumGenerator(); // Header stuff. @@ -64,8 +63,10 @@ class EnumGenerator { // Fills the name to use when declaring the enum. This is for use when // generating other .proto.h files. This code should be placed within the // enum's package namespace, but NOT within any class, even for nested - // enums. - void FillForwardDeclaration(set* enum_names); + // enums. A given key in enum_names will map from an enum class name to the + // EnumDescriptor that was responsible for its inclusion in the map. This can + // be used to associate the descriptor with the code generated for it. + void FillForwardDeclaration(map* enum_names); // Generate header code defining the enum. This code should be placed // within the enum's package namespace, but NOT within any class, even for @@ -94,10 +95,10 @@ class EnumGenerator { private: const EnumDescriptor* descriptor_; - string classname_; - Options options_; + const string classname_; + const Options& options_; // whether to generate the *_ARRAYSIZE constant. - bool generate_array_size_; + const bool generate_array_size_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator); }; diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc index 824e2205e..10252b392 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc @@ -36,6 +36,7 @@ #include #include #include +#include namespace google { namespace protobuf { @@ -58,10 +59,9 @@ void SetEnumVariables(const FieldDescriptor* descriptor, // =================================================================== -EnumFieldGenerator:: -EnumFieldGenerator(const FieldDescriptor* descriptor, - const Options& options) - : descriptor_(descriptor) { +EnumFieldGenerator::EnumFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) + : FieldGenerator(options), descriptor_(descriptor) { SetEnumVariables(descriptor, &variables_, options); } @@ -75,8 +75,8 @@ GeneratePrivateMembers(io::Printer* printer) const { void EnumFieldGenerator:: GenerateAccessorDeclarations(io::Printer* printer) const { printer->Print(variables_, - "$type$ $name$() const$deprecation$;\n" - "void set_$name$($type$ value)$deprecation$;\n"); + "$deprecated_attr$$type$ $name$() const;\n" + "$deprecated_attr$void set_$name$($type$ value);\n"); } void EnumFieldGenerator:: @@ -135,15 +135,16 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { printer->Print(variables_, "if ($type$_IsValid(value)) {\n" " set_$name$(static_cast< $type$ >(value));\n"); - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print(variables_, "} else {\n" " mutable_unknown_fields()->AddVarint($number$, value);\n"); } else { printer->Print( "} else {\n" - " unknown_fields_stream.WriteVarint32(tag);\n" - " unknown_fields_stream.WriteVarint32(value);\n"); + " unknown_fields_stream.WriteVarint32($tag$);\n" + " unknown_fields_stream.WriteVarint32(value);\n", + "tag", SimpleItoa(internal::WireFormat::MakeTag(descriptor_))); } printer->Print(variables_, "}\n"); @@ -228,10 +229,9 @@ GenerateConstructorCode(io::Printer* printer) const { // =================================================================== -RepeatedEnumFieldGenerator:: -RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, - const Options& options) - : descriptor_(descriptor) { +RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator( + const FieldDescriptor* descriptor, const Options& options) + : FieldGenerator(options), descriptor_(descriptor) { SetEnumVariables(descriptor, &variables_, options); } @@ -241,8 +241,8 @@ void RepeatedEnumFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { printer->Print(variables_, "::google::protobuf::RepeatedField $name$_;\n"); - if (descriptor_->is_packed() - && HasGeneratedMethods(descriptor_->file())) { + if (descriptor_->is_packed() && + HasGeneratedMethods(descriptor_->file(), options_)) { printer->Print(variables_, "mutable int _$name$_cached_byte_size_;\n"); } @@ -251,12 +251,12 @@ GeneratePrivateMembers(io::Printer* printer) const { void RepeatedEnumFieldGenerator:: GenerateAccessorDeclarations(io::Printer* printer) const { printer->Print(variables_, - "$type$ $name$(int index) const$deprecation$;\n" - "void set_$name$(int index, $type$ value)$deprecation$;\n" - "void add_$name$($type$ value)$deprecation$;\n"); + "$deprecated_attr$$type$ $name$(int index) const;\n" + "$deprecated_attr$void set_$name$(int index, $type$ value);\n" + "$deprecated_attr$void add_$name$($type$ value);\n"); printer->Print(variables_, - "const ::google::protobuf::RepeatedField& $name$() const$deprecation$;\n" - "::google::protobuf::RepeatedField* mutable_$name$()$deprecation$;\n"); + "$deprecated_attr$const ::google::protobuf::RepeatedField& $name$() const;\n" + "$deprecated_attr$::google::protobuf::RepeatedField* mutable_$name$();\n"); } void RepeatedEnumFieldGenerator:: @@ -335,7 +335,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { printer->Print(variables_, "if ($type$_IsValid(value)) {\n" " add_$name$(static_cast< $type$ >(value));\n"); - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print(variables_, "} else {\n" " mutable_unknown_fields()->AddVarint($number$, value);\n"); @@ -362,7 +362,7 @@ GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const { " NULL,\n" " NULL,\n" " this->mutable_$name$())));\n"); - } else if (UseUnknownFieldSet(descriptor_->file())) { + } else if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print(variables_, "DO_((::google::protobuf::internal::WireFormat::ReadPackedEnumPreserveUnknowns(\n" " input,\n" @@ -399,7 +399,7 @@ GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const { " if ($type$_IsValid(value)) {\n" " add_$name$(static_cast< $type$ >(value));\n" " } else {\n"); - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print(variables_, " mutable_unknown_fields()->AddVarint($number$, value);\n"); } else { diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.h b/src/google/protobuf/compiler/cpp/cpp_enum_field.h index 5b1d01ea8..fe21c5757 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.h @@ -46,8 +46,7 @@ namespace cpp { class EnumFieldGenerator : public FieldGenerator { public: - explicit EnumFieldGenerator(const FieldDescriptor* descriptor, - const Options& options); + EnumFieldGenerator(const FieldDescriptor* descriptor, const Options& options); ~EnumFieldGenerator(); // implements FieldGenerator --------------------------------------- @@ -74,8 +73,8 @@ class EnumFieldGenerator : public FieldGenerator { class EnumOneofFieldGenerator : public EnumFieldGenerator { public: - explicit EnumOneofFieldGenerator(const FieldDescriptor* descriptor, - const Options& options); + EnumOneofFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~EnumOneofFieldGenerator(); // implements FieldGenerator --------------------------------------- @@ -91,8 +90,8 @@ class EnumOneofFieldGenerator : public EnumFieldGenerator { class RepeatedEnumFieldGenerator : public FieldGenerator { public: - explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, - const Options& options); + RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~RepeatedEnumFieldGenerator(); // implements FieldGenerator --------------------------------------- diff --git a/src/google/protobuf/compiler/cpp/cpp_field.cc b/src/google/protobuf/compiler/cpp/cpp_field.cc index 8d47d4e08..b3ba3a2e5 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_field.cc @@ -77,6 +77,8 @@ void SetCommonFieldVariables(const FieldDescriptor* descriptor, WireFormat::TagSize(descriptor->number(), descriptor->type())); (*variables)["deprecation"] = descriptor->options().deprecated() ? " PROTOBUF_DEPRECATED" : ""; + (*variables)["deprecated_attr"] = descriptor->options().deprecated() + ? "PROTOBUF_DEPRECATED_ATTR " : ""; (*variables)["cppget"] = "Get"; @@ -121,6 +123,7 @@ GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const { FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor, const Options& options) : descriptor_(descriptor), + options_(options), field_generators_( new google::protobuf::scoped_ptr[descriptor->field_count()]) { // Construct all the FieldGenerators. diff --git a/src/google/protobuf/compiler/cpp/cpp_field.h b/src/google/protobuf/compiler/cpp/cpp_field.h index 1d7f82330..3b0125278 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_field.h @@ -69,7 +69,7 @@ void SetCommonOneofFieldVariables(const FieldDescriptor* descriptor, class FieldGenerator { public: - FieldGenerator() {} + explicit FieldGenerator(const Options& options) : options_(options) {} virtual ~FieldGenerator(); // Generate lines of code declaring members fields of the message class @@ -194,6 +194,9 @@ class FieldGenerator { // are placed in the message's ByteSize() method. virtual void GenerateByteSize(io::Printer* printer) const = 0; + protected: + const Options& options_; + private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGenerator); }; @@ -201,13 +204,14 @@ class FieldGenerator { // Convenience class which constructs FieldGenerators for a Descriptor. class FieldGeneratorMap { public: - explicit FieldGeneratorMap(const Descriptor* descriptor, const Options& options); + FieldGeneratorMap(const Descriptor* descriptor, const Options& options); ~FieldGeneratorMap(); const FieldGenerator& get(const FieldDescriptor* field) const; private: const Descriptor* descriptor_; + const Options& options_; google::protobuf::scoped_array > field_generators_; static FieldGenerator* MakeGenerator(const FieldDescriptor* field, diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index 37e4bae44..385b973ea 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -59,6 +59,7 @@ namespace cpp { FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options) : file_(file), + options_(options), message_generators_( new google::protobuf::scoped_ptr[file->message_type_count()]), enum_generators_( @@ -66,8 +67,7 @@ FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options) service_generators_( new google::protobuf::scoped_ptr[file->service_count()]), extension_generators_( - new google::protobuf::scoped_ptr[file->extension_count()]), - options_(options) { + new google::protobuf::scoped_ptr[file->extension_count()]) { for (int i = 0; i < file->message_type_count(); i++) { message_generators_[i].reset( @@ -94,7 +94,8 @@ FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options) FileGenerator::~FileGenerator() {} -void FileGenerator::GenerateProtoHeader(io::Printer* printer) { +void FileGenerator::GenerateProtoHeader(io::Printer* printer, + const string& info_path) { if (!options_.proto_h) { return; } @@ -114,6 +115,8 @@ void FileGenerator::GenerateProtoHeader(io::Printer* printer) { "dependency", dependency); } + GenerateMetadataPragma(printer, info_path); + printer->Print( "// @@protoc_insertion_point(includes)\n"); @@ -167,7 +170,8 @@ void FileGenerator::GenerateProtoHeader(io::Printer* printer) { GenerateBottomHeaderGuard(printer, filename_identifier); } -void FileGenerator::GeneratePBHeader(io::Printer* printer) { +void FileGenerator::GeneratePBHeader(io::Printer* printer, + const string& info_path) { string filename_identifier = FilenameIdentifier(file_->name() + (options_.proto_h ? ".pb.h" : "")); GenerateTopHeaderGuard(printer, filename_identifier); @@ -179,6 +183,7 @@ void FileGenerator::GeneratePBHeader(io::Printer* printer) { GenerateLibraryIncludes(printer); } GenerateDependencyIncludes(printer); + GenerateMetadataPragma(printer, info_path); printer->Print( "// @@protoc_insertion_point(includes)\n"); @@ -237,7 +242,7 @@ void FileGenerator::GeneratePBHeader(io::Printer* printer) { } void FileGenerator::GenerateSource(io::Printer* printer) { - bool well_known = IsWellKnownMessage(file_); + const bool use_system_include = IsWellKnownMessage(file_); string header = StripProto(file_->name()) + (options_.proto_h ? ".proto.h" : ".pb.h"); printer->Print( @@ -258,16 +263,16 @@ void FileGenerator::GenerateSource(io::Printer* printer) { "#include \n", "filename", file_->name(), "header", header, - "left", well_known ? "<" : "\"", - "right", well_known ? ">" : "\""); + "left", use_system_include ? "<" : "\"", + "right", use_system_include ? ">" : "\""); // Unknown fields implementation in lite mode uses StringOutputStream - if (!UseUnknownFieldSet(file_) && file_->message_type_count() > 0) { + if (!UseUnknownFieldSet(file_, options_) && file_->message_type_count() > 0) { printer->Print( "#include \n"); } - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, options_)) { printer->Print( "#include \n" "#include \n" @@ -292,7 +297,7 @@ void FileGenerator::GenerateSource(io::Printer* printer) { GenerateNamespaceOpeners(printer); - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, options_)) { printer->Print( "\n" "namespace {\n" @@ -306,7 +311,7 @@ void FileGenerator::GenerateSource(io::Printer* printer) { "name", ClassName(file_->enum_type(i), false)); } - if (HasGenericServices(file_)) { + if (HasGenericServices(file_, options_)) { for (int i = 0; i < file_->service_count(); i++) { printer->Print( "const ::google::protobuf::ServiceDescriptor* $name$_descriptor_ = NULL;\n", @@ -331,7 +336,7 @@ void FileGenerator::GenerateSource(io::Printer* printer) { // Generate classes. for (int i = 0; i < file_->message_type_count(); i++) { - if (i == 0 && HasGeneratedMethods(file_)) { + if (i == 0 && HasGeneratedMethods(file_, options_)) { printer->Print( "\n" "namespace {\n" @@ -356,7 +361,7 @@ void FileGenerator::GenerateSource(io::Printer* printer) { printer->Print("#endif // PROTOBUF_INLINE_NOT_IN_HEADERS\n"); } - if (HasGenericServices(file_)) { + if (HasGenericServices(file_, options_)) { // Generate services. for (int i = 0; i < file_->service_count(); i++) { if (i == 0) printer->Print("\n"); @@ -401,20 +406,24 @@ class FileGenerator::ForwardDeclarations { return ns; } - set& classes() { return classes_; } - set& enums() { return enums_; } + map& classes() { return classes_; } + map& enums() { return enums_; } void Print(io::Printer* printer) const { - for (set::const_iterator it = enums_.begin(), end = enums_.end(); + for (map::const_iterator + it = enums_.begin(), + end = enums_.end(); it != end; ++it) { - printer->Print("enum $enumname$ : int;\n" - "bool $enumname$_IsValid(int value);\n", - "enumname", it->c_str()); + printer->Print("enum $enumname$ : int;\n", "enumname", it->first); + printer->Annotate("enumname", it->second); + printer->Print("bool $enumname$_IsValid(int value);\n", "enumname", + it->first); } - for (set::const_iterator it = classes_.begin(), - end = classes_.end(); + for (map::const_iterator it = classes_.begin(), + end = classes_.end(); it != end; ++it) { - printer->Print("class $classname$;\n", "classname", it->c_str()); + printer->Print("class $classname$;\n", "classname", it->first); + printer->Annotate("classname", it->second); } for (map::const_iterator it = namespaces_.begin(), @@ -431,8 +440,8 @@ class FileGenerator::ForwardDeclarations { private: map namespaces_; - set classes_; - set enums_; + map classes_; + map enums_; }; void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { @@ -453,7 +462,7 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { // In optimize_for = LITE_RUNTIME mode, we don't generate AssignDescriptors() // and we only use AddDescriptors() to allocate default instances. - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, options_)) { printer->Print( "\n" "void $assigndescriptorsname$() {\n", @@ -486,7 +495,7 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { for (int i = 0; i < file_->enum_type_count(); i++) { enum_generators_[i]->GenerateDescriptorInitializer(printer, i); } - if (HasGenericServices(file_)) { + if (HasGenericServices(file_, options_)) { for (int i = 0; i < file_->service_count(); i++) { service_generators_[i]->GenerateDescriptorInitializer(printer, i); } @@ -552,22 +561,23 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { // Now generate the AddDescriptors() function. PrintHandlingOptionalStaticInitializers( - file_, printer, - // With static initializers. - // Note that we don't need any special synchronization in the following code - // because it is called at static init time before any threads exist. - "void $adddescriptorsname$() {\n" - " static bool already_here = false;\n" - " if (already_here) return;\n" - " already_here = true;\n" - " GOOGLE_PROTOBUF_VERIFY_VERSION;\n" - "\n", - // Without. - "void $adddescriptorsname$_impl() {\n" - " GOOGLE_PROTOBUF_VERIFY_VERSION;\n" - "\n", - // Vars. - "adddescriptorsname", GlobalAddDescriptorsName(file_->name())); + file_, options_, printer, + // With static initializers. + // Note that we don't need any special synchronization in the following + // code + // because it is called at static init time before any threads exist. + "void $adddescriptorsname$() {\n" + " static bool already_here = false;\n" + " if (already_here) return;\n" + " already_here = true;\n" + " GOOGLE_PROTOBUF_VERIFY_VERSION;\n" + "\n", + // Without. + "void $adddescriptorsname$_impl() {\n" + " GOOGLE_PROTOBUF_VERIFY_VERSION;\n" + "\n", + // Vars. + "adddescriptorsname", GlobalAddDescriptorsName(file_->name())); printer->Indent(); @@ -584,7 +594,7 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { "name", add_desc_name); } - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, options_)) { // Embed the descriptor. We simply serialize the entire FileDescriptorProto // and embed it as a string literal, which is parsed and built into real // descriptors at initialization time. @@ -678,23 +688,23 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { "\n"); PrintHandlingOptionalStaticInitializers( - file_, printer, - // With static initializers. - "// Force AddDescriptors() to be called at static initialization time.\n" - "struct StaticDescriptorInitializer_$filename$ {\n" - " StaticDescriptorInitializer_$filename$() {\n" - " $adddescriptorsname$();\n" - " }\n" - "} static_descriptor_initializer_$filename$_;\n", - // Without. - "GOOGLE_PROTOBUF_DECLARE_ONCE($adddescriptorsname$_once_);\n" - "void $adddescriptorsname$() {\n" - " ::google::protobuf::GoogleOnceInit(&$adddescriptorsname$_once_,\n" - " &$adddescriptorsname$_impl);\n" - "}\n", - // Vars. - "adddescriptorsname", GlobalAddDescriptorsName(file_->name()), - "filename", FilenameIdentifier(file_->name())); + file_, options_, printer, + // With static initializers. + "// Force AddDescriptors() to be called at static initialization time.\n" + "struct StaticDescriptorInitializer_$filename$ {\n" + " StaticDescriptorInitializer_$filename$() {\n" + " $adddescriptorsname$();\n" + " }\n" + "} static_descriptor_initializer_$filename$_;\n", + // Without. + "GOOGLE_PROTOBUF_DECLARE_ONCE($adddescriptorsname$_once_);\n" + "void $adddescriptorsname$() {\n" + " ::google::protobuf::GoogleOnceInit(&$adddescriptorsname$_once_,\n" + " &$adddescriptorsname$_impl);\n" + "}\n", + // Vars. + "adddescriptorsname", GlobalAddDescriptorsName(file_->name()), "filename", + FilenameIdentifier(file_->name())); } void FileGenerator::GenerateNamespaceOpeners(io::Printer* printer) { @@ -751,16 +761,15 @@ void FileGenerator::GenerateTopHeaderGuard(io::Printer* printer, const string& filename_identifier) { // Generate top of header. printer->Print( - "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" - "// source: $filename$\n" - "\n" - "#ifndef PROTOBUF_$filename_identifier$__INCLUDED\n" - "#define PROTOBUF_$filename_identifier$__INCLUDED\n" - "\n" - "#include \n" - "\n", - "filename", file_->name(), - "filename_identifier", filename_identifier); + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "// source: $filename$\n" + "\n" + "#ifndef PROTOBUF_$filename_identifier$__INCLUDED\n" + "#define PROTOBUF_$filename_identifier$__INCLUDED\n" + "\n" + "#include \n", + "filename", file_->name(), "filename_identifier", filename_identifier); + printer->Print("\n"); } void FileGenerator::GenerateBottomHeaderGuard( @@ -799,12 +808,12 @@ void FileGenerator::GenerateLibraryIncludes(io::Printer* printer) { "#include \n" "#include \n" "#include \n"); - if (UseUnknownFieldSet(file_)) { + if (UseUnknownFieldSet(file_, options_)) { printer->Print( "#include \n"); } if (file_->message_type_count() > 0) { - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, options_)) { printer->Print( "#include \n"); } else { @@ -818,7 +827,7 @@ void FileGenerator::GenerateLibraryIncludes(io::Printer* printer) { if (HasMapFields(file_)) { printer->Print( "#include \n"); - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, options_)) { printer->Print( "#include \n"); } else { @@ -828,7 +837,7 @@ void FileGenerator::GenerateLibraryIncludes(io::Printer* printer) { } if (HasEnumDefinitions(file_)) { - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, options_)) { printer->Print( "#include \n"); } else { @@ -837,12 +846,12 @@ void FileGenerator::GenerateLibraryIncludes(io::Printer* printer) { } } - if (HasGenericServices(file_)) { + if (HasGenericServices(file_, options_)) { printer->Print( "#include \n"); } - if (UseUnknownFieldSet(file_) && file_->message_type_count() > 0) { + if (UseUnknownFieldSet(file_, options_) && file_->message_type_count() > 0) { printer->Print( "#include \n"); } @@ -854,6 +863,19 @@ void FileGenerator::GenerateLibraryIncludes(io::Printer* printer) { } } +void FileGenerator::GenerateMetadataPragma(io::Printer* printer, + const string& info_path) { + if (!info_path.empty() && !options_.annotation_pragma_name.empty() && + !options_.annotation_guard_name.empty()) { + printer->Print( + "#ifdef $guard$\n" + "#pragma $pragma$ \"$info_path$\"\n" + "#endif // $guard$\n", + "guard", options_.annotation_guard_name, "pragma", + options_.annotation_pragma_name, "info_path", info_path); + } +} + void FileGenerator::GenerateDependencyIncludes(io::Printer* printer) { set public_import_names; for (int i = 0; i < file_->public_dependency_count(); i++) { @@ -861,16 +883,17 @@ void FileGenerator::GenerateDependencyIncludes(io::Printer* printer) { } for (int i = 0; i < file_->dependency_count(); i++) { - bool well_known = IsWellKnownMessage(file_->dependency(i)); + const bool use_system_include = IsWellKnownMessage(file_->dependency(i)); const string& name = file_->dependency(i)->name(); bool public_import = (public_import_names.count(name) != 0); + printer->Print( "#include $left$$dependency$.pb.h$right$$iwyu$\n", "dependency", StripProto(name), "iwyu", (public_import) ? " // IWYU pragma: export" : "", - "left", well_known ? "<" : "\"", - "right", well_known ? ">" : "\""); + "left", use_system_include ? "<" : "\"", + "right", use_system_include ? ">" : "\""); } } @@ -897,13 +920,15 @@ void FileGenerator::GenerateGlobalStateFunctionDeclarations( } void FileGenerator::GenerateMessageForwardDeclarations(io::Printer* printer) { - set classes; + map classes; for (int i = 0; i < file_->message_type_count(); i++) { message_generators_[i]->FillMessageForwardDeclarations(&classes); } - for (set::const_iterator it = classes.begin(), end = classes.end(); + for (map::const_iterator it = classes.begin(), + end = classes.end(); it != end; ++it) { - printer->Print("class $classname$;\n", "classname", it->c_str()); + printer->Print("class $classname$;\n", "classname", it->first); + printer->Annotate("classname", it->second); } } @@ -930,7 +955,7 @@ void FileGenerator::GenerateEnumDefinitions(io::Printer* printer) { } void FileGenerator::GenerateServiceDefinitions(io::Printer* printer) { - if (HasGenericServices(file_)) { + if (HasGenericServices(file_, options_)) { // Generate service definitions. for (int i = 0; i < file_->service_count(); i++) { if (i > 0) { diff --git a/src/google/protobuf/compiler/cpp/cpp_file.h b/src/google/protobuf/compiler/cpp/cpp_file.h index 29cdaea53..5dcf692bc 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.h +++ b/src/google/protobuf/compiler/cpp/cpp_file.h @@ -65,12 +65,17 @@ class ExtensionGenerator; // extension.h class FileGenerator { public: // See generator.cc for the meaning of dllexport_decl. - explicit FileGenerator(const FileDescriptor* file, - const Options& options); + FileGenerator(const FileDescriptor* file, const Options& options); ~FileGenerator(); - void GenerateProtoHeader(io::Printer* printer); - void GeneratePBHeader(io::Printer* printer); + // info_path, if non-empty, should be the path (relative to printer's output) + // to the metadata file describing this proto header. + void GenerateProtoHeader(io::Printer* printer, + const string& info_path); + // info_path, if non-empty, should be the path (relative to printer's output) + // to the metadata file describing this PB header. + void GeneratePBHeader(io::Printer* printer, + const string& info_path); void GenerateSource(io::Printer* printer); private: @@ -102,6 +107,10 @@ class FileGenerator { void GenerateLibraryIncludes(io::Printer* printer); void GenerateDependencyIncludes(io::Printer* printer); + // Generate a pragma to pull in metadata using the given info_path (if + // non-empty). info_path should be relative to printer's output. + void GenerateMetadataPragma(io::Printer* printer, const string& info_path); + // Generates a couple of different pieces before definitions: void GenerateGlobalStateFunctionDeclarations(io::Printer* printer); @@ -134,6 +143,7 @@ class FileGenerator { void GenerateProto2NamespaceEnumSpecializations(io::Printer* printer); const FileDescriptor* file_; + const Options options_; google::protobuf::scoped_array > message_generators_; google::protobuf::scoped_array > enum_generators_; @@ -142,7 +152,6 @@ class FileGenerator { // E.g. if the package is foo.bar, package_parts_ is {"foo", "bar"}. vector package_parts_; - const Options options_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator); }; diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.cc b/src/google/protobuf/compiler/cpp/cpp_generator.cc index 781526b50..31d189c25 100644 --- a/src/google/protobuf/compiler/cpp/cpp_generator.cc +++ b/src/google/protobuf/compiler/cpp/cpp_generator.cc @@ -90,6 +90,14 @@ bool CppGenerator::Generate(const FileDescriptor* file, file_options.dllexport_decl = options[i].second; } else if (options[i].first == "safe_boundary_check") { file_options.safe_boundary_check = true; + } else if (options[i].first == "annotate_headers") { + file_options.annotate_headers = true; + } else if (options[i].first == "annotation_pragma_name") { + file_options.annotation_pragma_name = options[i].second; + } else if (options[i].first == "annotation_guard_name") { + file_options.annotation_guard_name = options[i].second; + } else if (options[i].first == "lite") { + file_options.enforce_lite = true; } else { *error = "Unknown generator option: " + options[i].first; return false; @@ -107,16 +115,40 @@ bool CppGenerator::Generate(const FileDescriptor* file, if (file_options.proto_h) { google::protobuf::scoped_ptr output( generator_context->Open(basename + ".proto.h")); - io::Printer printer(output.get(), '$'); - file_generator.GenerateProtoHeader(&printer); + GeneratedCodeInfo annotations; + io::AnnotationProtoCollector annotation_collector( + &annotations); + string info_path = basename + ".proto.h.meta"; + io::Printer printer(output.get(), '$', file_options.annotate_headers + ? &annotation_collector + : NULL); + file_generator.GenerateProtoHeader( + &printer, file_options.annotate_headers ? info_path : ""); + if (file_options.annotate_headers) { + google::protobuf::scoped_ptr info_output( + generator_context->Open(info_path)); + annotations.SerializeToZeroCopyStream(info_output.get()); + } } basename.append(".pb"); { google::protobuf::scoped_ptr output( generator_context->Open(basename + ".h")); - io::Printer printer(output.get(), '$'); - file_generator.GeneratePBHeader(&printer); + GeneratedCodeInfo annotations; + io::AnnotationProtoCollector annotation_collector( + &annotations); + string info_path = basename + ".h.meta"; + io::Printer printer(output.get(), '$', file_options.annotate_headers + ? &annotation_collector + : NULL); + file_generator.GeneratePBHeader( + &printer, file_options.annotate_headers ? info_path : ""); + if (file_options.annotate_headers) { + google::protobuf::scoped_ptr info_output( + generator_context->Open(info_path)); + annotations.SerializeToZeroCopyStream(info_output.get()); + } } // Generate cc file. diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc index fb46e3879..2ad4d36a5 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.cc +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc @@ -68,14 +68,15 @@ const char* const kKeywordList[] = { "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "class", "compl", "const", "constexpr", "const_cast", "continue", "decltype", "default", "delete", "do", - "double", "dynamic_cast", "else", "enum", "explicit", "extern", "false", - "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", - "namespace", "new", "noexcept", "not", "not_eq", "NULL", "operator", "or", - "or_eq", "private", "protected", "public", "register", "reinterpret_cast", - "return", "short", "signed", "sizeof", "static", "static_assert", - "static_cast", "struct", "switch", "template", "this", "thread_local", - "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", - "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq" + "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", + "false", "float", "for", "friend", "goto", "if", "inline", "int", "long", + "mutable", "namespace", "new", "noexcept", "not", "not_eq", "NULL", + "operator", "or", "or_eq", "private", "protected", "public", "register", + "reinterpret_cast", "return", "short", "signed", "sizeof", "static", + "static_assert", "static_cast", "struct", "switch", "template", "this", + "thread_local", "throw", "true", "try", "typedef", "typeid", "typename", + "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", + "while", "xor", "xor_eq" }; hash_set MakeKeywordsMap() { @@ -171,9 +172,10 @@ string DependentBaseClassTemplateName(const Descriptor* descriptor) { return ClassName(descriptor, false) + "_InternalBase"; } -string SuperClassName(const Descriptor* descriptor) { - return HasDescriptorMethods(descriptor->file()) ? - "::google::protobuf::Message" : "::google::protobuf::MessageLite"; +string SuperClassName(const Descriptor* descriptor, const Options& options) { + return HasDescriptorMethods(descriptor->file(), options) + ? "::google::protobuf::Message" + : "::google::protobuf::MessageLite"; } string DependentBaseDownCast() { @@ -485,8 +487,9 @@ string SafeFunctionName(const Descriptor* descriptor, return function_name; } -bool StaticInitializersForced(const FileDescriptor* file) { - if (HasDescriptorMethods(file) || file->extension_count() > 0) { +bool StaticInitializersForced(const FileDescriptor* file, + const Options& options) { + if (HasDescriptorMethods(file, options) || file->extension_count() > 0) { return true; } for (int i = 0; i < file->message_type_count(); ++i) { @@ -498,10 +501,10 @@ bool StaticInitializersForced(const FileDescriptor* file) { } void PrintHandlingOptionalStaticInitializers( - const FileDescriptor* file, io::Printer* printer, + const FileDescriptor* file, const Options& options, io::Printer* printer, const char* with_static_init, const char* without_static_init, - const char* var1, const string& val1, - const char* var2, const string& val2) { + const char* var1, const string& val1, const char* var2, + const string& val2) { map vars; if (var1) { vars[var1] = val1; @@ -510,14 +513,16 @@ void PrintHandlingOptionalStaticInitializers( vars[var2] = val2; } PrintHandlingOptionalStaticInitializers( - vars, file, printer, with_static_init, without_static_init); + vars, file, options, printer, with_static_init, without_static_init); } -void PrintHandlingOptionalStaticInitializers( - const map& vars, const FileDescriptor* file, - io::Printer* printer, const char* with_static_init, - const char* without_static_init) { - if (StaticInitializersForced(file)) { +void PrintHandlingOptionalStaticInitializers(const map& vars, + const FileDescriptor* file, + const Options& options, + io::Printer* printer, + const char* with_static_init, + const char* without_static_init) { + if (StaticInitializersForced(file, options)) { printer->Print(vars, with_static_init); } else { printer->Print(vars, (string( @@ -612,10 +617,11 @@ enum Utf8CheckMode { }; // Which level of UTF-8 enforcemant is placed on this file. -static Utf8CheckMode GetUtf8CheckMode(const FieldDescriptor* field) { +static Utf8CheckMode GetUtf8CheckMode(const FieldDescriptor* field, + const Options& options) { if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3) { return STRICT; - } else if (field->file()->options().optimize_for() != + } else if (GetOptimizeFor(field->file(), options) != FileOptions::LITE_RUNTIME) { return VERIFY; } else { @@ -624,13 +630,13 @@ static Utf8CheckMode GetUtf8CheckMode(const FieldDescriptor* field) { } static void GenerateUtf8CheckCode(const FieldDescriptor* field, - bool for_parse, + const Options& options, bool for_parse, const map& variables, const char* parameters, const char* strict_function, const char* verify_function, io::Printer* printer) { - switch (GetUtf8CheckMode(field)) { + switch (GetUtf8CheckMode(field, options)) { case STRICT: { if (for_parse) { printer->Print("DO_("); @@ -674,23 +680,22 @@ static void GenerateUtf8CheckCode(const FieldDescriptor* field, } void GenerateUtf8CheckCodeForString(const FieldDescriptor* field, - bool for_parse, + const Options& options, bool for_parse, const map& variables, const char* parameters, io::Printer* printer) { - GenerateUtf8CheckCode(field, for_parse, variables, parameters, + GenerateUtf8CheckCode(field, options, for_parse, variables, parameters, "VerifyUtf8String", "VerifyUTF8StringNamedField", printer); } void GenerateUtf8CheckCodeForCord(const FieldDescriptor* field, - bool for_parse, + const Options& options, bool for_parse, const map& variables, const char* parameters, io::Printer* printer) { - GenerateUtf8CheckCode(field, for_parse, variables, parameters, - "VerifyUtf8Cord", "VerifyUTF8CordNamedField", - printer); + GenerateUtf8CheckCode(field, options, for_parse, variables, parameters, + "VerifyUtf8Cord", "VerifyUTF8CordNamedField", printer); } } // namespace cpp diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.h b/src/google/protobuf/compiler/cpp/cpp_helpers.h index a22d414d0..018acfcad 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.h +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h @@ -37,8 +37,9 @@ #include #include -#include +#include #include +#include namespace google { namespace protobuf { @@ -72,7 +73,7 @@ string DependentBaseClassTemplateName(const Descriptor* descriptor); // Name of the base class: either the dependent base class (for use with // proto_h) or google::protobuf::Message. -string SuperClassName(const Descriptor* descriptor); +string SuperClassName(const Descriptor* descriptor, const Options& options); // Returns a string that down-casts from the dependent base class to the // derived class. @@ -118,7 +119,7 @@ string DependentTypeName(const FieldDescriptor* field); string FieldMessageTypeName(const FieldDescriptor* field); // Strips ".proto" or ".protodevel" from the end of a filename. -string StripProto(const string& filename); +LIBPROTOC_EXPORT string StripProto(const string& filename); // Get the C++ type name for a primitive type (e.g. "double", "::google::protobuf::int32", etc.). // Note: non-built-in type names will be qualified, meaning they will start @@ -168,12 +169,17 @@ inline bool PreserveUnknownFields(const Descriptor* message) { return message->file()->syntax() != FileDescriptor::SYNTAX_PROTO3; } +// Returns the optimize mode for , respecting . +::google::protobuf::FileOptions_OptimizeMode GetOptimizeFor( + const FileDescriptor* file, const Options& options); + // If PreserveUnknownFields() is true, determines whether unknown // fields will be stored in an UnknownFieldSet or a string. // If PreserveUnknownFields() is false, this method will not be // used. -inline bool UseUnknownFieldSet(const FileDescriptor* file) { - return file->options().optimize_for() != FileOptions::LITE_RUNTIME; +inline bool UseUnknownFieldSet(const FileDescriptor* file, + const Options& options) { + return GetOptimizeFor(file, options) != FileOptions::LITE_RUNTIME; } @@ -186,45 +192,52 @@ bool HasEnumDefinitions(const FileDescriptor* file); // Does this file have generated parsing, serialization, and other // standard methods for which reflection-based fallback implementations exist? -inline bool HasGeneratedMethods(const FileDescriptor* file) { - return file->options().optimize_for() != FileOptions::CODE_SIZE; +inline bool HasGeneratedMethods(const FileDescriptor* file, + const Options& options) { + return GetOptimizeFor(file, options) != FileOptions::CODE_SIZE; } // Do message classes in this file have descriptor and reflection methods? -inline bool HasDescriptorMethods(const FileDescriptor* file) { - return file->options().optimize_for() != FileOptions::LITE_RUNTIME; +inline bool HasDescriptorMethods(const FileDescriptor* file, + const Options& options) { + return GetOptimizeFor(file, options) != FileOptions::LITE_RUNTIME; } // Should we generate generic services for this file? -inline bool HasGenericServices(const FileDescriptor* file) { +inline bool HasGenericServices(const FileDescriptor* file, + const Options& options) { return file->service_count() > 0 && - file->options().optimize_for() != FileOptions::LITE_RUNTIME && + GetOptimizeFor(file, options) != FileOptions::LITE_RUNTIME && file->options().cc_generic_services(); } // Should we generate a separate, super-optimized code path for serializing to // flat arrays? We don't do this in Lite mode because we'd rather reduce code // size. -inline bool HasFastArraySerialization(const FileDescriptor* file) { - return file->options().optimize_for() == FileOptions::SPEED; +inline bool HasFastArraySerialization(const FileDescriptor* file, + const Options& options) { + return GetOptimizeFor(file, options) == FileOptions::SPEED; } // Returns whether we have to generate code with static initializers. -bool StaticInitializersForced(const FileDescriptor* file); +bool StaticInitializersForced(const FileDescriptor* file, + const Options& options); // Prints 'with_static_init' if static initializers have to be used for the // provided file. Otherwise emits both 'with_static_init' and // 'without_static_init' using #ifdef. void PrintHandlingOptionalStaticInitializers( - const FileDescriptor* file, io::Printer* printer, + const FileDescriptor* file, const Options& options, io::Printer* printer, const char* with_static_init, const char* without_static_init, - const char* var1 = NULL, const string& val1 = "", - const char* var2 = NULL, const string& val2 = ""); + const char* var1 = NULL, const string& val1 = "", const char* var2 = NULL, + const string& val2 = ""); -void PrintHandlingOptionalStaticInitializers( - const map& vars, const FileDescriptor* file, - io::Printer* printer, const char* with_static_init, - const char* without_static_init); +void PrintHandlingOptionalStaticInitializers(const map& vars, + const FileDescriptor* file, + const Options& options, + io::Printer* printer, + const char* with_static_init, + const char* without_static_init); inline bool IsMapEntryMessage(const Descriptor* descriptor) { @@ -267,19 +280,23 @@ bool IsAnyMessage(const Descriptor* descriptor); bool IsWellKnownMessage(const FileDescriptor* descriptor); -void GenerateUtf8CheckCodeForString( - const FieldDescriptor* field, - bool for_parse, - const map& variables, - const char* parameters, - io::Printer* printer); +void GenerateUtf8CheckCodeForString(const FieldDescriptor* field, + const Options& options, bool for_parse, + const map& variables, + const char* parameters, + io::Printer* printer); -void GenerateUtf8CheckCodeForCord( - const FieldDescriptor* field, - bool for_parse, - const map& variables, - const char* parameters, - io::Printer* printer); +void GenerateUtf8CheckCodeForCord(const FieldDescriptor* field, + const Options& options, bool for_parse, + const map& variables, + const char* parameters, io::Printer* printer); + +inline ::google::protobuf::FileOptions_OptimizeMode GetOptimizeFor( + const FileDescriptor* file, const Options& options) { + return options.enforce_lite + ? FileOptions::LITE_RUNTIME + : file->options().optimize_for(); +} } // namespace cpp } // namespace compiler diff --git a/src/google/protobuf/compiler/cpp/cpp_map_field.cc b/src/google/protobuf/compiler/cpp/cpp_map_field.cc index e5e2f07dd..f585c31b4 100644 --- a/src/google/protobuf/compiler/cpp/cpp_map_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_map_field.cc @@ -49,10 +49,11 @@ void SetMessageVariables(const FieldDescriptor* descriptor, const Options& options) { SetCommonFieldVariables(descriptor, variables, options); (*variables)["type"] = FieldMessageTypeName(descriptor); - (*variables)["stream_writer"] = (*variables)["declared_type"] + - (HasFastArraySerialization(descriptor->message_type()->file()) ? - "MaybeToArray" : - ""); + (*variables)["stream_writer"] = + (*variables)["declared_type"] + + (HasFastArraySerialization(descriptor->message_type()->file(), options) + ? "MaybeToArray" + : ""); (*variables)["full_name"] = descriptor->full_name(); const FieldDescriptor* key = @@ -83,7 +84,7 @@ void SetMessageVariables(const FieldDescriptor* descriptor, (*variables)["number"] = SimpleItoa(descriptor->number()); (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); - if (HasDescriptorMethods(descriptor->file())) { + if (HasDescriptorMethods(descriptor->file(), options)) { (*variables)["lite"] = ""; } else { (*variables)["lite"] = "Lite"; @@ -98,10 +99,10 @@ void SetMessageVariables(const FieldDescriptor* descriptor, } } -MapFieldGenerator:: -MapFieldGenerator(const FieldDescriptor* descriptor, - const Options& options) - : descriptor_(descriptor), +MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) + : FieldGenerator(options), + descriptor_(descriptor), dependent_field_(options.proto_h && IsFieldDependent(descriptor)) { SetMessageVariables(descriptor, &variables_, options); } @@ -127,10 +128,10 @@ GeneratePrivateMembers(io::Printer* printer) const { void MapFieldGenerator:: GenerateAccessorDeclarations(io::Printer* printer) const { printer->Print(variables_, - "const ::google::protobuf::Map< $key_cpp$, $val_cpp$ >&\n" - " $name$() const$deprecation$;\n" - "::google::protobuf::Map< $key_cpp$, $val_cpp$ >*\n" - " mutable_$name$()$deprecation$;\n"); + "$deprecated_attr$const ::google::protobuf::Map< $key_cpp$, $val_cpp$ >&\n" + " $name$() const;\n" + "$deprecated_attr$::google::protobuf::Map< $key_cpp$, $val_cpp$ >*\n" + " mutable_$name$();\n"); } void MapFieldGenerator:: @@ -170,7 +171,7 @@ GenerateSwappingCode(io::Printer* printer) const { void MapFieldGenerator:: GenerateConstructorCode(io::Printer* printer) const { - if (HasDescriptorMethods(descriptor_->file())) { + if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print(variables_, "$name$_.SetAssignDescriptorCallback(\n" " protobuf_AssignDescriptorsOnce);\n" @@ -217,7 +218,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { " (*mutable_$name$())[entry->key()] =\n" " static_cast< $val_cpp$ >(*entry->mutable_value());\n" " } else {\n"); - if (HasDescriptorMethods(descriptor_->file())) { + if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print(variables_, " mutable_unknown_fields()" "->AddLengthDelimited($number$, data);\n"); @@ -238,14 +239,14 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { descriptor_->message_type()->FindFieldByName("key"); if (key_field->type() == FieldDescriptor::TYPE_STRING) { GenerateUtf8CheckCodeForString( - key_field, true, variables_, + key_field, options_, true, variables_, "entry->key().data(), entry->key().length(),\n", printer); } if (value_field->type() == FieldDescriptor::TYPE_STRING) { - GenerateUtf8CheckCodeForString( - value_field, true, variables_, - "entry->mutable_value()->data(),\n" - "entry->mutable_value()->length(),\n", printer); + GenerateUtf8CheckCodeForString(value_field, options_, true, variables_, + "entry->mutable_value()->data(),\n" + "entry->mutable_value()->length(),\n", + printer); } // If entry is allocated by arena, its desctructor should be avoided. @@ -285,14 +286,14 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { const FieldDescriptor* value_field = descriptor_->message_type()->FindFieldByName("value"); if (key_field->type() == FieldDescriptor::TYPE_STRING) { - GenerateUtf8CheckCodeForString( - key_field, false, variables_, - "it->first.data(), it->first.length(),\n", printer); + GenerateUtf8CheckCodeForString(key_field, options_, false, variables_, + "it->first.data(), it->first.length(),\n", + printer); } if (value_field->type() == FieldDescriptor::TYPE_STRING) { - GenerateUtf8CheckCodeForString( - value_field, false, variables_, - "it->second.data(), it->second.length(),\n", printer); + GenerateUtf8CheckCodeForString(value_field, options_, false, variables_, + "it->second.data(), it->second.length(),\n", + printer); } printer->Outdent(); @@ -343,14 +344,14 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { const FieldDescriptor* value_field = descriptor_->message_type()->FindFieldByName("value"); if (key_field->type() == FieldDescriptor::TYPE_STRING) { - GenerateUtf8CheckCodeForString( - key_field, false, variables_, - "it->first.data(), it->first.length(),\n", printer); + GenerateUtf8CheckCodeForString(key_field, options_, false, variables_, + "it->first.data(), it->first.length(),\n", + printer); } if (value_field->type() == FieldDescriptor::TYPE_STRING) { - GenerateUtf8CheckCodeForString( - value_field, false, variables_, - "it->second.data(), it->second.length(),\n", printer); + GenerateUtf8CheckCodeForString(value_field, options_, false, variables_, + "it->second.data(), it->second.length(),\n", + printer); } printer->Outdent(); diff --git a/src/google/protobuf/compiler/cpp/cpp_map_field.h b/src/google/protobuf/compiler/cpp/cpp_map_field.h index 5e205623b..087dcde0f 100644 --- a/src/google/protobuf/compiler/cpp/cpp_map_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_map_field.h @@ -43,8 +43,7 @@ namespace cpp { class MapFieldGenerator : public FieldGenerator { public: - explicit MapFieldGenerator(const FieldDescriptor* descriptor, - const Options& options); + MapFieldGenerator(const FieldDescriptor* descriptor, const Options& options); ~MapFieldGenerator(); // implements FieldGenerator --------------------------------------- diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index 8304ebbda..da2a4c922 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -106,8 +106,8 @@ struct ExtensionRangeSorter { // Returns true if the "required" restriction check should be ignored for the // given field. -inline static bool ShouldIgnoreRequiredFieldCheck( - const FieldDescriptor* field) { +inline static bool ShouldIgnoreRequiredFieldCheck(const FieldDescriptor* field, + const Options& options) { return false; } @@ -116,9 +116,8 @@ inline static bool ShouldIgnoreRequiredFieldCheck( // // already_seen is used to avoid checking the same type multiple times // (and also to protect against recursion). -static bool HasRequiredFields( - const Descriptor* type, - hash_set* already_seen) { +static bool HasRequiredFields(const Descriptor* type, const Options& options, + hash_set* already_seen) { if (already_seen->count(type) > 0) { // Since the first occurrence of a required field causes the whole // function to return true, we can assume that if the type is already @@ -138,8 +137,8 @@ static bool HasRequiredFields( return true; } if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && - !ShouldIgnoreRequiredFieldCheck(field)) { - if (HasRequiredFields(field->message_type(), already_seen)) { + !ShouldIgnoreRequiredFieldCheck(field, options)) { + if (HasRequiredFields(field->message_type(), options, already_seen)) { return true; } } @@ -148,9 +147,9 @@ static bool HasRequiredFields( return false; } -static bool HasRequiredFields(const Descriptor* type) { +static bool HasRequiredFields(const Descriptor* type, const Options& options) { hash_set already_seen; - return HasRequiredFields(type, &already_seen); + return HasRequiredFields(type, options, &already_seen); } // This returns an estimate of the compiler's alignment for the field. This @@ -423,8 +422,8 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, MessageGenerator::~MessageGenerator() {} void MessageGenerator:: -FillMessageForwardDeclarations(set* class_names) { - class_names->insert(classname_); +FillMessageForwardDeclarations(map* class_names) { + (*class_names)[classname_] = descriptor_; for (int i = 0; i < descriptor_->nested_type_count(); i++) { // map entry message doesn't need forward declaration. Since map entry @@ -436,7 +435,7 @@ FillMessageForwardDeclarations(set* class_names) { } void MessageGenerator:: -FillEnumForwardDeclarations(set* enum_names) { +FillEnumForwardDeclarations(map* enum_names) { for (int i = 0; i < descriptor_->nested_type_count(); i++) { nested_generators_[i]->FillEnumForwardDeclarations(enum_names); } @@ -480,7 +479,7 @@ GenerateDependentFieldAccessorDeclarations(io::Printer* printer) { // If the message is dependent, the inline clear_*() method will need // to delete the message type, so it must be in the dependent base // class. (See also GenerateFieldAccessorDeclarations.) - printer->Print(vars, "void clear_$name$()$deprecation$;\n"); + printer->Print(vars, "$deprecated_attr$void clear_$name$();\n"); } // Generate type-specific accessor declarations. field_generators_.get(field).GenerateDependentAccessorDeclarations(printer); @@ -515,22 +514,24 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) { } if (field->is_repeated()) { - printer->Print(vars, "int $name$_size() const$deprecation$;\n"); + printer->Print(vars, "$deprecated_attr$int $name$_size() const;\n"); } else if (HasHasMethod(field)) { - printer->Print(vars, "bool has_$name$() const$deprecation$;\n"); + printer->Print(vars, "$deprecated_attr$bool has_$name$() const;\n"); } else if (HasPrivateHasMethod(field)) { printer->Print(vars, "private:\n" - "bool has_$name$() const$deprecation$;\n" + "bool has_$name$() const;\n" "public:\n"); } if (!dependent_field) { // If this field is dependent, then its clear_() method is in the // depenent base class. (See also GenerateDependentAccessorDeclarations.) - printer->Print(vars, "void clear_$name$()$deprecation$;\n"); + printer->Print(vars, "$deprecated_attr$void clear_$name$();\n"); } - printer->Print(vars, "static const int $constant_name$ = $number$;\n"); + printer->Print(vars, + "$deprecated_attr$static const int $constant_name$ = " + "$number$;\n"); // Generate type-specific accessor declarations. field_generators_.get(field).GenerateAccessorDeclarations(printer); @@ -837,7 +838,7 @@ GenerateDependentBaseClassDefinition(io::Printer* printer) { map vars; vars["classname"] = DependentBaseClassTemplateName(descriptor_); - vars["superclass"] = SuperClassName(descriptor_); + vars["superclass"] = SuperClassName(descriptor_, options_); printer->Print(vars, "template \n" @@ -888,10 +889,11 @@ GenerateClassDefinition(io::Printer* printer) { vars["superclass"] = DependentBaseClassTemplateName(descriptor_) + "<" + classname_ + ">"; } else { - vars["superclass"] = SuperClassName(descriptor_); + vars["superclass"] = SuperClassName(descriptor_, options_); } printer->Print(vars, "class $dllexport$$classname$ : public $superclass$ {\n"); + printer->Annotate("classname", descriptor_); if (use_dependent_base_) { printer->Print(vars, " friend class $superclass$;\n"); } @@ -911,7 +913,7 @@ GenerateClassDefinition(io::Printer* printer) { "\n"); if (PreserveUnknownFields(descriptor_)) { - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( "inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {\n" " return _internal_metadata_.unknown_fields();\n" @@ -965,7 +967,7 @@ GenerateClassDefinition(io::Printer* printer) { } // Only generate this member if it's not disabled. - if (HasDescriptorMethods(descriptor_->file()) && + if (HasDescriptorMethods(descriptor_->file(), options_) && !descriptor_->options().no_standard_descriptor_accessor()) { printer->Print(vars, "static const ::google::protobuf::Descriptor* descriptor();\n"); @@ -1002,7 +1004,7 @@ GenerateClassDefinition(io::Printer* printer) { "\n"); } - if (!StaticInitializersForced(descriptor_->file())) { + if (!StaticInitializersForced(descriptor_->file(), options_)) { printer->Print(vars, "#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER\n" "// Returns the internal default instance pointer. This function can\n" @@ -1027,6 +1029,8 @@ GenerateClassDefinition(io::Printer* printer) { "// implements Any -----------------------------------------------\n" "\n" "void PackFrom(const ::google::protobuf::Message& message);\n" + "void PackFrom(const ::google::protobuf::Message& message,\n" + " const ::std::string& type_url_prefix);\n" "bool UnpackTo(::google::protobuf::Message* message) const;\n" "template bool Is() const {\n" " return _any_metadata_.Is();\n" @@ -1043,8 +1047,8 @@ GenerateClassDefinition(io::Printer* printer) { "\n" "$classname$* New(::google::protobuf::Arena* arena) const;\n"); - if (HasGeneratedMethods(descriptor_->file())) { - if (HasDescriptorMethods(descriptor_->file())) { + if (HasGeneratedMethods(descriptor_->file(), options_)) { + if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print(vars, "void CopyFrom(const ::google::protobuf::Message& from);\n" "void MergeFrom(const ::google::protobuf::Message& from);\n"); @@ -1066,11 +1070,11 @@ GenerateClassDefinition(io::Printer* printer) { " ::google::protobuf::io::CodedOutputStream* output) const;\n"); // DiscardUnknownFields() is implemented in message.cc using reflections. We // need to implement this function in generated code for messages. - if (!UseUnknownFieldSet(descriptor_->file())) { + if (!UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( "void DiscardUnknownFields();\n"); } - if (HasFastArraySerialization(descriptor_->file())) { + if (HasFastArraySerialization(descriptor_->file(), options_)) { printer->Print( "::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;\n"); } @@ -1093,7 +1097,7 @@ GenerateClassDefinition(io::Printer* printer) { } uses_string_ = false; if (PreserveUnknownFields(descriptor_) && - !UseUnknownFieldSet(descriptor_->file())) { + !UseUnknownFieldSet(descriptor_->file(), options_)) { uses_string_ = true; } for (int i = 0; i < descriptors.size(); i++) { @@ -1123,7 +1127,7 @@ GenerateClassDefinition(io::Printer* printer) { "classname", classname_); } - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( "private:\n" "inline ::google::protobuf::Arena* GetArenaNoVirtual() const {\n" @@ -1147,7 +1151,7 @@ GenerateClassDefinition(io::Printer* printer) { "\n"); } - if (HasDescriptorMethods(descriptor_->file())) { + if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print( "::google::protobuf::Metadata GetMetadata() const;\n" "\n"); @@ -1236,7 +1240,7 @@ GenerateClassDefinition(io::Printer* printer) { "oneof_name", descriptor_->oneof_decl(i)->name()); } - if (HasGeneratedMethods(descriptor_->file()) && + if (HasGeneratedMethods(descriptor_->file(), options_) && !descriptor_->options().message_set_wire_format() && num_required_fields_ > 1) { printer->Print( @@ -1278,7 +1282,7 @@ GenerateClassDefinition(io::Printer* printer) { "\n"); } - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( "::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;\n"); } else { @@ -1405,15 +1409,14 @@ GenerateClassDefinition(io::Printer* printer) { // friends so that they can access private static variables like // default_instance_ and reflection_. PrintHandlingOptionalStaticInitializers( - descriptor_->file(), printer, - // With static initializers. - "friend void $dllexport_decl$ $adddescriptorsname$();\n", - // Without. - "friend void $dllexport_decl$ $adddescriptorsname$_impl();\n", - // Vars. - "dllexport_decl", options_.dllexport_decl, - "adddescriptorsname", - GlobalAddDescriptorsName(descriptor_->file()->name())); + descriptor_->file(), options_, printer, + // With static initializers. + "friend void $dllexport_decl$ $adddescriptorsname$();\n", + // Without. + "friend void $dllexport_decl$ $adddescriptorsname$_impl();\n", + // Vars. + "dllexport_decl", options_.dllexport_decl, "adddescriptorsname", + GlobalAddDescriptorsName(descriptor_->file()->name())); printer->Print( "friend void $assigndescriptorsname$();\n" @@ -1576,7 +1579,7 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { // Unknown field offset: either points to the unknown field set if embedded // directly, or indicates that the unknown field set is stored as part of the // internal metadata if not. - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print(vars, " -1,\n"); } else { @@ -1616,7 +1619,7 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { // arena pointer and unknown field set (in a space-efficient way) if we use // that implementation strategy, or an offset directly to the arena pointer if // not (because e.g. we don't have an unknown field set). - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print(vars, " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" "$classname$, _internal_metadata_),\n"); @@ -1706,7 +1709,7 @@ GenerateDefaultInstanceAllocator(io::Printer* printer) { "classname", classname_); if ((descriptor_->oneof_decl_count() > 0) && - HasDescriptorMethods(descriptor_->file())) { + HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print( "$classname$_default_oneof_instance_ = new $classname$OneofInstance();\n", "classname", classname_); @@ -1746,7 +1749,7 @@ GenerateShutdownCode(io::Printer* printer) { "delete $classname$::default_instance_;\n", "classname", classname_); - if (HasDescriptorMethods(descriptor_->file())) { + if (HasDescriptorMethods(descriptor_->file(), options_)) { if (descriptor_->oneof_decl_count() > 0) { printer->Print( "delete $classname$_default_oneof_instance_;\n", @@ -1774,7 +1777,8 @@ void MessageGenerator:: GenerateClassMethods(io::Printer* printer) { // mutable_unknown_fields wrapper function for LazyStringOutputStream // callback. - if (!UseUnknownFieldSet(descriptor_->file())) { + if (PreserveUnknownFields(descriptor_) && + !UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( "static ::std::string* MutableUnknownFieldsFor$classname$(\n" " $classname$* ptr) {\n" @@ -1789,6 +1793,11 @@ GenerateClassMethods(io::Printer* printer) { " _any_metadata_.PackFrom(message);\n" "}\n" "\n" + "void $classname$::PackFrom(const ::google::protobuf::Message& message,\n" + " const ::std::string& type_url_prefix) {\n" + " _any_metadata_.PackFrom(message, type_url_prefix);\n" + "}\n" + "\n" "bool $classname$::UnpackTo(::google::protobuf::Message* message) const {\n" " return _any_metadata_.UnpackTo(message);\n" "}\n" @@ -1843,7 +1852,7 @@ GenerateClassMethods(io::Printer* printer) { printer->Print("\n"); } - if (HasGeneratedMethods(descriptor_->file())) { + if (HasGeneratedMethods(descriptor_->file(), options_)) { GenerateClear(printer); printer->Print("\n"); @@ -1853,7 +1862,7 @@ GenerateClassMethods(io::Printer* printer) { GenerateSerializeWithCachedSizes(printer); printer->Print("\n"); - if (HasFastArraySerialization(descriptor_->file())) { + if (HasFastArraySerialization(descriptor_->file(), options_)) { GenerateSerializeWithCachedSizesToArray(printer); printer->Print("\n"); } @@ -1874,7 +1883,7 @@ GenerateClassMethods(io::Printer* printer) { GenerateSwap(printer); printer->Print("\n"); - if (HasDescriptorMethods(descriptor_->file())) { + if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print( "::google::protobuf::Metadata $classname$::GetMetadata() const {\n" " protobuf_AssignDescriptorsOnce();\n" @@ -1899,11 +1908,10 @@ GenerateClassMethods(io::Printer* printer) { void MessageGenerator:: GenerateOffsets(io::Printer* printer) { - printer->Print( - "static const int $classname$_offsets_[$field_count$] = {\n", - "classname", classname_, - "field_count", SimpleItoa(max( - 1, descriptor_->field_count() + descriptor_->oneof_decl_count()))); + printer->Print("static const int $classname$_offsets_[$field_count$] = {\n", + "classname", classname_, "field_count", + SimpleItoa(std::max(1, descriptor_->field_count() + + descriptor_->oneof_decl_count()))); printer->Indent(); for (int i = 0; i < descriptor_->field_count(); i++) { @@ -1952,7 +1960,7 @@ GenerateSharedConstructorCode(io::Printer* printer) { "_cached_size_ = 0;\n").c_str()); if (PreserveUnknownFields(descriptor_) && - !UseUnknownFieldSet(descriptor_->file())) { + !UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( "_unknown_fields_.UnsafeSetDefault(\n" " &::google::protobuf::internal::GetEmptyStringAlreadyInited());\n"); @@ -1997,7 +2005,7 @@ GenerateSharedDestructorCode(io::Printer* printer) { // Write the desctructor for _unknown_fields_ in lite runtime. if (PreserveUnknownFields(descriptor_) && - !UseUnknownFieldSet(descriptor_->file())) { + !UseUnknownFieldSet(descriptor_->file(), options_)) { if (SupportsArenas(descriptor_)) { printer->Print( "_unknown_fields_.Destroy(\n" @@ -2028,11 +2036,11 @@ GenerateSharedDestructorCode(io::Printer* printer) { } PrintHandlingOptionalStaticInitializers( - descriptor_->file(), printer, - // With static initializers. - "if (this != default_instance_) {\n", - // Without. - "if (this != &default_instance()) {\n"); + descriptor_->file(), options_, printer, + // With static initializers. + "if (this != default_instance_) {\n", + // Without. + "if (this != &default_instance()) {\n"); // We need to delete all embedded messages. // TODO(kenton): If we make unset messages point at default instances @@ -2112,7 +2120,7 @@ GenerateStructors(io::Printer* printer) { superclass = DependentBaseClassTemplateName(descriptor_) + "<" + classname_ + ">"; } else { - superclass = SuperClassName(descriptor_); + superclass = SuperClassName(descriptor_, options_); } string initializer_with_arena = superclass + "()"; @@ -2120,7 +2128,7 @@ GenerateStructors(io::Printer* printer) { initializer_with_arena += ",\n _extensions_(arena)"; } - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { initializer_with_arena += ",\n _internal_metadata_(arena)"; } else { initializer_with_arena += ",\n _arena_ptr_(arena)"; @@ -2140,7 +2148,7 @@ GenerateStructors(io::Printer* printer) { } string initializer_null; - initializer_null = (UseUnknownFieldSet(descriptor_->file()) ? + initializer_null = (UseUnknownFieldSet(descriptor_->file(), options_) ? ", _internal_metadata_(NULL)" : ", _arena_ptr_(NULL)"); if (IsAnyMessage(descriptor_)) { initializer_null += ", _any_metadata_(&type_url_, &value_)"; @@ -2194,24 +2202,23 @@ GenerateStructors(io::Printer* printer) { if (!field->is_repeated() && field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && (field->containing_oneof() == NULL || - HasDescriptorMethods(descriptor_->file()))) { + HasDescriptorMethods(descriptor_->file(), options_))) { string name; if (field->containing_oneof()) { name = classname_ + "_default_oneof_instance_->"; } name += FieldName(field); PrintHandlingOptionalStaticInitializers( - descriptor_->file(), printer, - // With static initializers. - " $name$_ = const_cast< $type$*>(&$type$::default_instance());\n", - // Without. - " $name$_ = const_cast< $type$*>(\n" - " $type$::internal_default_instance());\n", - // Vars. - "name", name, - "type", FieldMessageTypeName(field)); + descriptor_->file(), options_, printer, + // With static initializers. + " $name$_ = const_cast< $type$*>(&$type$::default_instance());\n", + // Without. + " $name$_ = const_cast< $type$*>(\n" + " $type$::internal_default_instance());\n", + // Vars. + "name", name, "type", FieldMessageTypeName(field)); } else if (field->containing_oneof() && - HasDescriptorMethods(descriptor_->file())) { + HasDescriptorMethods(descriptor_->file(), options_)) { field_generators_.get(descriptor_->field(i)) .GenerateConstructorCode(printer); } @@ -2227,10 +2234,10 @@ GenerateStructors(io::Printer* printer) { "classname", classname_, "superclass", superclass, "full_name", descriptor_->full_name()); - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( ",\n _internal_metadata_(NULL)"); - } else if (!UseUnknownFieldSet(descriptor_->file())) { + } else if (!UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print(",\n _arena_ptr_(NULL)"); } if (IsAnyMessage(descriptor_)) { @@ -2278,7 +2285,7 @@ GenerateStructors(io::Printer* printer) { "classname", classname_); // Only generate this member if it's not disabled. - if (HasDescriptorMethods(descriptor_->file()) && + if (HasDescriptorMethods(descriptor_->file(), options_) && !descriptor_->options().no_standard_descriptor_accessor()) { printer->Print( "const ::google::protobuf::Descriptor* $classname$::descriptor() {\n" @@ -2296,14 +2303,14 @@ GenerateStructors(io::Printer* printer) { "classname", classname_); PrintHandlingOptionalStaticInitializers( - descriptor_->file(), printer, - // With static initializers. - " if (default_instance_ == NULL) $adddescriptorsname$();\n", - // Without. - " $adddescriptorsname$();\n", - // Vars. - "adddescriptorsname", - GlobalAddDescriptorsName(descriptor_->file()->name())); + descriptor_->file(), options_, printer, + // With static initializers. + " if (default_instance_ == NULL) $adddescriptorsname$();\n", + // Without. + " $adddescriptorsname$();\n", + // Vars. + "adddescriptorsname", + GlobalAddDescriptorsName(descriptor_->file()->name())); printer->Print( " return *default_instance_;\n" @@ -2345,8 +2352,10 @@ static int popcnt(uint32 n) { void MessageGenerator:: GenerateClear(io::Printer* printer) { - printer->Print("void $classname$::Clear() {\n", - "classname", classname_); + printer->Print( + "void $classname$::Clear() {\n" + "// @@protoc_insertion_point(message_clear_start:$full_name$)\n", + "classname", classname_, "full_name", descriptor_->full_name()); printer->Indent(); // Step 1: Extensions @@ -2383,8 +2392,16 @@ GenerateClear(io::Printer* printer) { // positions of two fields in the Message. // ZR_ zeroes a non-empty range of fields via memset. const char* macros = + "#if defined(__clang__)\n" + "#define ZR_HELPER_(f) \\\n" + " _Pragma(\"clang diagnostic push\") \\\n" + " _Pragma(\"clang diagnostic ignored \\\"-Winvalid-offsetof\\\"\") \\\n" + " __builtin_offsetof($classname$, f) \\\n" + " _Pragma(\"clang diagnostic pop\")\n" + "#else\n" "#define ZR_HELPER_(f) reinterpret_cast(\\\n" - " &reinterpret_cast<$classname$*>(16)->f)\n\n" + " &reinterpret_cast<$classname$*>(16)->f)\n" + "#endif\n\n" "#define ZR_(first, last) do {\\\n" " ::memset(&first, 0,\\\n" " ZR_HELPER_(last) - ZR_HELPER_(first) + sizeof(last));\\\n" @@ -2512,7 +2529,7 @@ GenerateClear(io::Printer* printer) { } if (PreserveUnknownFields(descriptor_)) { - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( "if (_internal_metadata_.have_unknown_fields()) {\n" " mutable_unknown_fields()->Clear();\n" @@ -2542,10 +2559,13 @@ GenerateOneofClear(io::Printer* printer) { map oneof_vars; oneof_vars["classname"] = classname_; oneof_vars["oneofname"] = descriptor_->oneof_decl(i)->name(); + oneof_vars["full_name"] = descriptor_->full_name(); string message_class; printer->Print(oneof_vars, - "void $classname$::clear_$oneofname$() {\n"); + "void $classname$::clear_$oneofname$() {\n" + "// @@protoc_insertion_point(one_of_clear_start:" + "$full_name$)\n"); printer->Indent(); printer->Print(oneof_vars, "switch($oneofname$_case()) {\n"); @@ -2628,7 +2648,7 @@ GenerateSwap(io::Printer* printer) { "classname", classname_); printer->Indent(); - if (HasGeneratedMethods(descriptor_->file())) { + if (HasGeneratedMethods(descriptor_->file(), options_)) { for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); field_generators_.get(field).GenerateSwappingCode(printer); @@ -2649,19 +2669,15 @@ GenerateSwap(io::Printer* printer) { } } - if (PreserveUnknownFields(descriptor_)) { - if (UseUnknownFieldSet(descriptor_->file())) { - printer->Print( - "_internal_metadata_.Swap(&other->_internal_metadata_);\n"); - } else { - printer->Print("_unknown_fields_.Swap(&other->_unknown_fields_);\n"); - } - } else { - // Still swap internal_metadata as it may contain more than just - // unknown fields. + // Ignore PreserveUnknownFields here - always swap internal_metadata as it + // may contain more than just unknown fields. + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( - "_internal_metadata_.Swap(&other->_internal_metadata_);\n"); + "_internal_metadata_.Swap(&other->_internal_metadata_);\n"); + } else { + printer->Print("_unknown_fields_.Swap(&other->_unknown_fields_);\n"); } + printer->Print("std::swap(_cached_size_, other->_cached_size_);\n"); if (descriptor_->extension_range_count() > 0) { printer->Print("_extensions_.Swap(&other->_extensions_);\n"); @@ -2676,13 +2692,15 @@ GenerateSwap(io::Printer* printer) { void MessageGenerator:: GenerateMergeFrom(io::Printer* printer) { - if (HasDescriptorMethods(descriptor_->file())) { + if (HasDescriptorMethods(descriptor_->file(), options_)) { // Generate the generalized MergeFrom (aka that which takes in the Message // base class as a parameter). printer->Print( - "void $classname$::MergeFrom(const ::google::protobuf::Message& from) {\n" - " if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);\n", - "classname", classname_); + "void $classname$::MergeFrom(const ::google::protobuf::Message& from) {\n" + "// @@protoc_insertion_point(generalized_merge_from_start:" + "$full_name$)\n" + " if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);\n", + "classname", classname_, "full_name", descriptor_->full_name()); printer->Indent(); // Cast the message to the proper type. If we find that the message is @@ -2694,11 +2712,15 @@ GenerateMergeFrom(io::Printer* printer) { " ::google::protobuf::internal::DynamicCastToGenerated(\n" " &from);\n" "if (source == NULL) {\n" + "// @@protoc_insertion_point(generalized_merge_from_cast_fail:" + "$full_name$)\n" " ::google::protobuf::internal::ReflectionOps::Merge(from, this);\n" "} else {\n" + "// @@protoc_insertion_point(generalized_merge_from_cast_success:" + "$full_name$)\n" " MergeFrom(*source);\n" "}\n", - "classname", classname_); + "classname", classname_, "full_name", descriptor_->full_name()); printer->Outdent(); printer->Print("}\n\n"); @@ -2715,9 +2737,11 @@ GenerateMergeFrom(io::Printer* printer) { // Generate the class-specific MergeFrom, which avoids the GOOGLE_CHECK and cast. printer->Print( - "void $classname$::MergeFrom(const $classname$& from) {\n" - " if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);\n", - "classname", classname_); + "void $classname$::MergeFrom(const $classname$& from) {\n" + "// @@protoc_insertion_point(class_specific_merge_from_start:" + "$full_name$)\n" + " if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);\n", + "classname", classname_, "full_name", descriptor_->full_name()); printer->Indent(); // Merge Repeated fields. These fields do not require a @@ -2818,7 +2842,7 @@ GenerateMergeFrom(io::Printer* printer) { } if (PreserveUnknownFields(descriptor_)) { - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( "if (from._internal_metadata_.have_unknown_fields()) {\n" " mutable_unknown_fields()->MergeFrom(from.unknown_fields());\n" @@ -2837,12 +2861,14 @@ GenerateMergeFrom(io::Printer* printer) { void MessageGenerator:: GenerateCopyFrom(io::Printer* printer) { - if (HasDescriptorMethods(descriptor_->file())) { + if (HasDescriptorMethods(descriptor_->file(), options_)) { // Generate the generalized CopyFrom (aka that which takes in the Message // base class as a parameter). printer->Print( - "void $classname$::CopyFrom(const ::google::protobuf::Message& from) {\n", - "classname", classname_); + "void $classname$::CopyFrom(const ::google::protobuf::Message& from) {\n" + "// @@protoc_insertion_point(generalized_copy_from_start:" + "$full_name$)\n", + "classname", classname_, "full_name", descriptor_->full_name()); printer->Indent(); printer->Print( @@ -2856,8 +2882,10 @@ GenerateCopyFrom(io::Printer* printer) { // Generate the class-specific CopyFrom. printer->Print( - "void $classname$::CopyFrom(const $classname$& from) {\n", - "classname", classname_); + "void $classname$::CopyFrom(const $classname$& from) {\n" + "// @@protoc_insertion_point(class_specific_copy_from_start:" + "$full_name$)\n", + "classname", classname_, "full_name", descriptor_->full_name()); printer->Indent(); printer->Print( @@ -2879,15 +2907,15 @@ GenerateMergeFromCodedStream(io::Printer* printer) { "classname", classname_); PrintHandlingOptionalStaticInitializers( - descriptor_->file(), printer, - // With static initializers. - " return _extensions_.ParseMessageSet(input, default_instance_,\n" - " mutable_unknown_fields());\n", - // Without. - " return _extensions_.ParseMessageSet(input, &default_instance(),\n" - " mutable_unknown_fields());\n", - // Vars. - "classname", classname_); + descriptor_->file(), options_, printer, + // With static initializers. + " return _extensions_.ParseMessageSet(input, default_instance_,\n" + " mutable_unknown_fields());\n", + // Without. + " return _extensions_.ParseMessageSet(input, &default_instance(),\n" + " mutable_unknown_fields());\n", + // Vars. + "classname", classname_); printer->Print( "}\n"); @@ -2897,11 +2925,12 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Print( "bool $classname$::MergePartialFromCodedStream(\n" " ::google::protobuf::io::CodedInputStream* input) {\n" - "#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure\n" + "#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure\n" " ::google::protobuf::uint32 tag;\n", "classname", classname_); - if (!UseUnknownFieldSet(descriptor_->file())) { + if (PreserveUnknownFields(descriptor_) && + !UseUnknownFieldSet(descriptor_->file(), options_)) { // Use LazyStringOutputString to avoid initializing unknown fields string // unless it is actually needed. For the same reason, disable eager refresh // on the CodedOutputStream. @@ -3129,32 +3158,32 @@ GenerateMergeFromCodedStream(io::Printer* printer) { } printer->Print(") {\n"); if (PreserveUnknownFields(descriptor_)) { - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { PrintHandlingOptionalStaticInitializers( - descriptor_->file(), printer, - // With static initializers. - " DO_(_extensions_.ParseField(tag, input, default_instance_,\n" - " mutable_unknown_fields()));\n", - // Without. - " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n" - " mutable_unknown_fields()));\n"); + descriptor_->file(), options_, printer, + // With static initializers. + " DO_(_extensions_.ParseField(tag, input, default_instance_,\n" + " mutable_unknown_fields()));\n", + // Without. + " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n" + " mutable_unknown_fields()));\n"); } else { PrintHandlingOptionalStaticInitializers( - descriptor_->file(), printer, - // With static initializers. - " DO_(_extensions_.ParseField(tag, input, default_instance_,\n" - " &unknown_fields_stream));\n", - // Without. - " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n" - " &unknown_fields_stream));\n"); + descriptor_->file(), options_, printer, + // With static initializers. + " DO_(_extensions_.ParseField(tag, input, default_instance_,\n" + " &unknown_fields_stream));\n", + // Without. + " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n" + " &unknown_fields_stream));\n"); } } else { PrintHandlingOptionalStaticInitializers( - descriptor_->file(), printer, - // With static initializers. - " DO_(_extensions_.ParseField(tag, input, default_instance_);\n", - // Without. - " DO_(_extensions_.ParseField(tag, input, &default_instance());\n"); + descriptor_->file(), options_, printer, + // With static initializers. + " DO_(_extensions_.ParseField(tag, input, default_instance_);\n", + // Without. + " DO_(_extensions_.ParseField(tag, input, &default_instance());\n"); } printer->Print( " continue;\n" @@ -3163,7 +3192,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) { // We really don't recognize this tag. Skip it. if (PreserveUnknownFields(descriptor_)) { - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( "DO_(::google::protobuf::internal::WireFormat::SkipField(\n" " input, tag, mutable_unknown_fields()));\n"); @@ -3256,7 +3285,7 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) { " ::google::protobuf::io::CodedOutputStream* output) const {\n" " _extensions_.SerializeMessageSetWithCachedSizes(output);\n", "classname", classname_); - GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file())); + GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file(), options_)); printer->Print( " ::google::protobuf::internal::WireFormat::SerializeUnknownMessageSetItems(\n" " unknown_fields(), output);\n"); @@ -3296,7 +3325,7 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) { " target =\n" " _extensions_.SerializeMessageSetWithCachedSizesToArray(target);\n", "classname", classname_); - GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file())); + GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file(), options_)); printer->Print( " target = ::google::protobuf::internal::WireFormat::\n" " SerializeUnknownMessageSetItemsToArray(\n" @@ -3362,7 +3391,7 @@ GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) { } if (PreserveUnknownFields(descriptor_)) { - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print("if (_internal_metadata_.have_unknown_fields()) {\n"); printer->Indent(); if (to_array) { @@ -3429,10 +3458,11 @@ GenerateByteSize(io::Printer* printer) { if (descriptor_->options().message_set_wire_format()) { // Special-case MessageSet. printer->Print( - "int $classname$::ByteSize() const {\n" - " int total_size = _extensions_.MessageSetByteSize();\n", - "classname", classname_); - GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file())); + "int $classname$::ByteSize() const {\n" + "// @@protoc_insertion_point(message_set_byte_size_start:$full_name$)\n" + " int total_size = _extensions_.MessageSetByteSize();\n", + "classname", classname_, "full_name", descriptor_->full_name()); + GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file(), options_)); printer->Print( "if (_internal_metadata_.have_unknown_fields()) {\n" " total_size += ::google::protobuf::internal::WireFormat::\n" @@ -3451,8 +3481,10 @@ GenerateByteSize(io::Printer* printer) { // Emit a function (rarely used, we hope) that handles the required fields // by checking for each one individually. printer->Print( - "int $classname$::RequiredFieldsByteSizeFallback() const {\n", - "classname", classname_); + "int $classname$::RequiredFieldsByteSizeFallback() const {\n" + "// @@protoc_insertion_point(required_fields_byte_size_fallback_start:" + "$full_name$)\n", + "classname", classname_, "full_name", descriptor_->full_name()); printer->Indent(); printer->Print("int total_size = 0;\n"); for (int i = 0; i < descriptor_->field_count(); i++) { @@ -3475,8 +3507,9 @@ GenerateByteSize(io::Printer* printer) { } printer->Print( - "int $classname$::ByteSize() const {\n", - "classname", classname_); + "int $classname$::ByteSize() const {\n" + "// @@protoc_insertion_point(message_byte_size_start:$full_name$)\n", + "classname", classname_, "full_name", descriptor_->full_name()); printer->Indent(); printer->Print( "int total_size = 0;\n" @@ -3652,7 +3685,7 @@ GenerateByteSize(io::Printer* printer) { } if (PreserveUnknownFields(descriptor_)) { - if (UseUnknownFieldSet(descriptor_->file())) { + if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( "if (_internal_metadata_.have_unknown_fields()) {\n" " total_size +=\n" @@ -3718,8 +3751,8 @@ GenerateIsInitialized(io::Printer* printer) { for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && - !ShouldIgnoreRequiredFieldCheck(field) && - HasRequiredFields(field->message_type())) { + !ShouldIgnoreRequiredFieldCheck(field, options_) && + HasRequiredFields(field->message_type(), options_)) { if (field->is_repeated()) { printer->Print( "if (!::google::protobuf::internal::AllAreInitialized(this->$name$()))" diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h index 8e19a3f05..31223e13f 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.h +++ b/src/google/protobuf/compiler/cpp/cpp_message.h @@ -67,9 +67,13 @@ class MessageGenerator { // Header stuff. // Return names for foward declarations of this class and all its nested - // types. - void FillMessageForwardDeclarations(set* class_names); - void FillEnumForwardDeclarations(set* enum_names); + // types. A given key in {class,enum}_names will map from a class name to the + // descriptor that was responsible for its inclusion in the map. This can be + // used to associate the descriptor with the code generated for it. + void FillMessageForwardDeclarations( + map* class_names); + void FillEnumForwardDeclarations( + map* enum_names); // Generate definitions of all nested enums (must come before class // definitions because those classes use the enums definitions). diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.cc b/src/google/protobuf/compiler/cpp/cpp_message_field.cc index b45458920..332c0264f 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc @@ -53,10 +53,11 @@ void SetMessageVariables(const FieldDescriptor* descriptor, (*variables)["non_null_ptr_to_name"] = StrCat("this->", (*variables)["name"], "_"); } - (*variables)["stream_writer"] = (*variables)["declared_type"] + - (HasFastArraySerialization(descriptor->message_type()->file()) ? - "MaybeToArray" : - ""); + (*variables)["stream_writer"] = + (*variables)["declared_type"] + + (HasFastArraySerialization(descriptor->message_type()->file(), options) + ? "MaybeToArray" + : ""); // NOTE: Escaped here to unblock proto1->proto2 migration. // TODO(liujisi): Extend this to apply for other conflicting methods. (*variables)["release_name"] = @@ -77,11 +78,11 @@ void SetMessageVariables(const FieldDescriptor* descriptor, // =================================================================== -MessageFieldGenerator:: -MessageFieldGenerator(const FieldDescriptor* descriptor, - const Options& options) - : descriptor_(descriptor), - dependent_field_(options.proto_h && IsFieldDependent(descriptor)) { +MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) + : FieldGenerator(options), + descriptor_(descriptor), + dependent_field_(options.proto_h && IsFieldDependent(descriptor)) { SetMessageVariables(descriptor, &variables_, options); } @@ -95,7 +96,7 @@ GeneratePrivateMembers(io::Printer* printer) const { void MessageFieldGenerator:: GenerateGetterDeclaration(io::Printer* printer) const { printer->Print(variables_, - "const $type$& $name$() const$deprecation$;\n"); + "$deprecated_attr$const $type$& $name$() const;\n"); } void MessageFieldGenerator:: @@ -105,9 +106,9 @@ GenerateDependentAccessorDeclarations(io::Printer* printer) const { } // Arena manipulation code is out-of-line in the derived message class. printer->Print(variables_, - "$type$* mutable_$name$()$deprecation$;\n" - "$type$* $release_name$()$deprecation$;\n" - "void set_allocated_$name$($type$* $name$)$deprecation$;\n"); + "$deprecated_attr$$type$* mutable_$name$();\n" + "$deprecated_attr$$type$* $release_name$();\n" + "$deprecated_attr$void set_allocated_$name$($type$* $name$);\n"); } void MessageFieldGenerator:: @@ -115,28 +116,28 @@ GenerateAccessorDeclarations(io::Printer* printer) const { if (SupportsArenas(descriptor_)) { printer->Print(variables_, "private:\n" - "void _slow_mutable_$name$()$deprecation$;\n"); + "void _slow_mutable_$name$();\n"); if (SupportsArenas(descriptor_->message_type())) { printer->Print(variables_, "void _slow_set_allocated_$name$(\n" - " ::google::protobuf::Arena* message_arena, $type$** $name$)$deprecation$;\n"); + " ::google::protobuf::Arena* message_arena, $type$** $name$);\n"); } printer->Print(variables_, - "$type$* _slow_$release_name$()$deprecation$;\n" + "$type$* _slow_$release_name$();\n" "public:\n"); } GenerateGetterDeclaration(printer); if (!dependent_field_) { printer->Print(variables_, - "$type$* mutable_$name$()$deprecation$;\n" - "$type$* $release_name$()$deprecation$;\n" - "void set_allocated_$name$($type$* $name$)$deprecation$;\n"); + "$deprecated_attr$$type$* mutable_$name$();\n" + "$deprecated_attr$$type$* $release_name$();\n" + "$deprecated_attr$void set_allocated_$name$($type$* $name$);\n"); } if (SupportsArenas(descriptor_)) { printer->Print(variables_, - "$type$* unsafe_arena_release_$name$()$deprecation$;\n" - "void unsafe_arena_set_allocated_$name$(\n" - " $type$* $name$)$deprecation$;\n"); + "$deprecated_attr$$type$* unsafe_arena_release_$name$();\n" + "$deprecated_attr$void unsafe_arena_set_allocated_$name$(\n" + " $type$* $name$);\n"); } } @@ -167,6 +168,7 @@ void MessageFieldGenerator::GenerateNonInlineAccessorDefinitions( " }\n" "}\n" "$type$* $classname$::unsafe_arena_release_$name$() {\n" + " // @@protoc_insertion_point(field_unsafe_arena_release:$full_name$)\n" " $clear_hasbit$\n" " $type$* temp = $name$_;\n" " $name$_ = NULL;\n" @@ -246,6 +248,7 @@ GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { "}\n" "template \n" "inline $type$* $dependent_classname$::$release_name$() {\n" + " // @@protoc_insertion_point(field_release:$full_name$)\n" " $dependent_typename$*& $name$_ = $this_message$$name$_;\n" " $clear_hasbit$\n" " if ($this_message$GetArenaNoVirtual() != NULL) {\n" @@ -305,6 +308,7 @@ GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { "}\n" "template \n" "inline $type$* $dependent_classname$::$release_name$() {\n" + " // @@protoc_insertion_point(field_release:$full_name$)\n" " $clear_hasbit$\n" " $dependent_typename$*& $name$_ = $this_message$$name$_;\n" " $dependent_typename$* temp = $name$_;\n" @@ -349,11 +353,11 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " // @@protoc_insertion_point(field_get:$full_name$)\n"); PrintHandlingOptionalStaticInitializers( - variables, descriptor_->file(), printer, - // With static initializers. - " return $name$_ != NULL ? *$name$_ : *default_instance_->$name$_;\n", - // Without. - " return $name$_ != NULL ? *$name$_ : *default_instance().$name$_;\n"); + variables, descriptor_->file(), options_, printer, + // With static initializers. + " return $name$_ != NULL ? *$name$_ : *default_instance_->$name$_;\n", + // Without. + " return $name$_ != NULL ? *$name$_ : *default_instance().$name$_;\n"); printer->Print(variables, "}\n"); if (dependent_field_) { @@ -373,6 +377,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, "}\n" "$inline$" "$type$* $classname$::$release_name$() {\n" + " // @@protoc_insertion_point(field_release:$full_name$)\n" " $clear_hasbit$\n" " if (GetArenaNoVirtual() != NULL) {\n" " return _slow_$release_name$();\n" @@ -426,6 +431,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, "}\n" "$inline$" "$type$* $classname$::$release_name$() {\n" + " // @@protoc_insertion_point(field_release:$full_name$)\n" " $clear_hasbit$\n" " $type$* temp = $name$_;\n" " $name$_ = NULL;\n" @@ -547,7 +553,7 @@ GenerateDependentAccessorDeclarations(io::Printer* printer) const { return; } printer->Print(variables_, - "const $type$& $name$() const$deprecation$;\n"); + "$deprecated_attr$const $type$& $name$() const;\n"); MessageFieldGenerator::GenerateDependentAccessorDeclarations(printer); } @@ -560,7 +566,7 @@ GenerateGetterDeclaration(io::Printer* printer) const { return; } printer->Print(variables_, - "const $type$& $name$() const$deprecation$;\n"); + "$deprecated_attr$const $type$& $name$() const;\n"); } void MessageOneofFieldGenerator:: @@ -651,6 +657,7 @@ InternalGenerateInlineAccessorDefinitions(const map& variables, "$tmpl$" "$inline$" "$type$* $dependent_classname$::$release_name$() {\n" + " // @@protoc_insertion_point(field_release:$full_name$)\n" " if ($this_message$has_$name$()) {\n" " $this_message$clear_has_$oneof_name$();\n" " if ($this_message$GetArenaNoVirtual() != NULL) {\n" @@ -706,6 +713,8 @@ InternalGenerateInlineAccessorDefinitions(const map& variables, " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" "}\n" "$inline$ $type$* $classname$::unsafe_arena_release_$name$() {\n" + " // @@protoc_insertion_point(field_unsafe_arena_release" + ":$full_name$)\n" " if (has_$name$()) {\n" " clear_has_$oneof_name$();\n" " $type$* temp = $oneof_prefix$$name$_;\n" @@ -744,6 +753,7 @@ InternalGenerateInlineAccessorDefinitions(const map& variables, "$tmpl$" "$inline$" "$type$* $dependent_classname$::$release_name$() {\n" + " // @@protoc_insertion_point(field_release:$full_name$)\n" " if ($this_message$has_$name$()) {\n" " $this_message$clear_has_$oneof_name$();\n" " $dependent_typename$* temp = $field_member$;\n" @@ -805,12 +815,12 @@ GenerateConstructorCode(io::Printer* printer) const { // =================================================================== -RepeatedMessageFieldGenerator:: -RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, - const Options& options) - : descriptor_(descriptor), - dependent_field_(options.proto_h && IsFieldDependent(descriptor)), - dependent_getter_(dependent_field_ && options.safe_boundary_check) { +RepeatedMessageFieldGenerator::RepeatedMessageFieldGenerator( + const FieldDescriptor* descriptor, const Options& options) + : FieldGenerator(options), + descriptor_(descriptor), + dependent_field_(options.proto_h && IsFieldDependent(descriptor)), + dependent_getter_(dependent_field_ && options.safe_boundary_check) { SetMessageVariables(descriptor, &variables_, options); } @@ -825,23 +835,23 @@ GeneratePrivateMembers(io::Printer* printer) const { void RepeatedMessageFieldGenerator:: InternalGenerateTypeDependentAccessorDeclarations(io::Printer* printer) const { printer->Print(variables_, - "$type$* mutable_$name$(int index)$deprecation$;\n" - "$type$* add_$name$()$deprecation$;\n"); + "$deprecated_attr$$type$* mutable_$name$(int index);\n" + "$deprecated_attr$$type$* add_$name$();\n"); if (dependent_getter_) { printer->Print(variables_, - "const ::google::protobuf::RepeatedPtrField< $type$ >&\n" - " $name$() const$deprecation$;\n"); + "$deprecated_attr$const ::google::protobuf::RepeatedPtrField< $type$ >&\n" + " $name$() const;\n"); } printer->Print(variables_, - "::google::protobuf::RepeatedPtrField< $type$ >*\n" - " mutable_$name$()$deprecation$;\n"); + "$deprecated_attr$::google::protobuf::RepeatedPtrField< $type$ >*\n" + " mutable_$name$();\n"); } void RepeatedMessageFieldGenerator:: GenerateDependentAccessorDeclarations(io::Printer* printer) const { if (dependent_getter_) { printer->Print(variables_, - "const $type$& $name$(int index) const$deprecation$;\n"); + "$deprecated_attr$const $type$& $name$(int index) const;\n"); } if (dependent_field_) { InternalGenerateTypeDependentAccessorDeclarations(printer); @@ -852,15 +862,15 @@ void RepeatedMessageFieldGenerator:: GenerateAccessorDeclarations(io::Printer* printer) const { if (!dependent_getter_) { printer->Print(variables_, - "const $type$& $name$(int index) const$deprecation$;\n"); + "$deprecated_attr$const $type$& $name$(int index) const;\n"); } if (!dependent_field_) { InternalGenerateTypeDependentAccessorDeclarations(printer); } if (!dependent_getter_) { printer->Print(variables_, - "const ::google::protobuf::RepeatedPtrField< $type$ >&\n" - " $name$() const$deprecation$;\n"); + "$deprecated_attr$const ::google::protobuf::RepeatedPtrField< $type$ >&\n" + " $name$() const;\n"); } } diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.h b/src/google/protobuf/compiler/cpp/cpp_message_field.h index 35efd0fa7..d8d9279c9 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.h @@ -46,8 +46,8 @@ namespace cpp { class MessageFieldGenerator : public FieldGenerator { public: - explicit MessageFieldGenerator(const FieldDescriptor* descriptor, - const Options& options); + MessageFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~MessageFieldGenerator(); // implements FieldGenerator --------------------------------------- @@ -83,8 +83,8 @@ class MessageFieldGenerator : public FieldGenerator { class MessageOneofFieldGenerator : public MessageFieldGenerator { public: - explicit MessageOneofFieldGenerator(const FieldDescriptor* descriptor, - const Options& options); + MessageOneofFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~MessageOneofFieldGenerator(); // implements FieldGenerator --------------------------------------- @@ -110,8 +110,8 @@ class MessageOneofFieldGenerator : public MessageFieldGenerator { class RepeatedMessageFieldGenerator : public FieldGenerator { public: - explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, - const Options& options); + RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~RepeatedMessageFieldGenerator(); // implements FieldGenerator --------------------------------------- diff --git a/src/google/protobuf/compiler/cpp/cpp_options.h b/src/google/protobuf/compiler/cpp/cpp_options.h index 4463f2006..ab1d2ed38 100644 --- a/src/google/protobuf/compiler/cpp/cpp_options.h +++ b/src/google/protobuf/compiler/cpp/cpp_options.h @@ -43,11 +43,19 @@ namespace cpp { // Generator options (see generator.cc for a description of each): struct Options { - Options() : safe_boundary_check(false), proto_h(false) { - } + Options() + : safe_boundary_check(false), + proto_h(false), + annotate_headers(false), + enforce_lite(false) {} + string dllexport_decl; bool safe_boundary_check; bool proto_h; + bool annotate_headers; + bool enforce_lite; + string annotation_pragma_name; + string annotation_guard_name; }; } // namespace cpp diff --git a/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc index d1efbfe6d..34a41d823 100644 --- a/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_plugin_unittest.cc @@ -44,9 +44,10 @@ #include #include +#include +#include #include #include -#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc index 9f929d378..650f0381e 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc @@ -100,10 +100,9 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, // =================================================================== -PrimitiveFieldGenerator:: -PrimitiveFieldGenerator(const FieldDescriptor* descriptor, - const Options& options) - : descriptor_(descriptor) { +PrimitiveFieldGenerator::PrimitiveFieldGenerator( + const FieldDescriptor* descriptor, const Options& options) + : FieldGenerator(options), descriptor_(descriptor) { SetPrimitiveVariables(descriptor, &variables_, options); } @@ -117,8 +116,8 @@ GeneratePrivateMembers(io::Printer* printer) const { void PrimitiveFieldGenerator:: GenerateAccessorDeclarations(io::Printer* printer) const { printer->Print(variables_, - "$type$ $name$() const$deprecation$;\n" - "void set_$name$($type$ value)$deprecation$;\n"); + "$deprecated_attr$$type$ $name$() const;\n" + "$deprecated_attr$void set_$name$($type$ value);\n"); } void PrimitiveFieldGenerator:: @@ -256,10 +255,9 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { // =================================================================== -RepeatedPrimitiveFieldGenerator:: -RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, - const Options& options) - : descriptor_(descriptor) { +RepeatedPrimitiveFieldGenerator::RepeatedPrimitiveFieldGenerator( + const FieldDescriptor* descriptor, const Options& options) + : FieldGenerator(options), descriptor_(descriptor) { SetPrimitiveVariables(descriptor, &variables_, options); if (descriptor->is_packed()) { @@ -277,7 +275,8 @@ void RepeatedPrimitiveFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { printer->Print(variables_, "::google::protobuf::RepeatedField< $type$ > $name$_;\n"); - if (descriptor_->is_packed() && HasGeneratedMethods(descriptor_->file())) { + if (descriptor_->is_packed() && + HasGeneratedMethods(descriptor_->file(), options_)) { printer->Print(variables_, "mutable int _$name$_cached_byte_size_;\n"); } @@ -286,14 +285,14 @@ GeneratePrivateMembers(io::Printer* printer) const { void RepeatedPrimitiveFieldGenerator:: GenerateAccessorDeclarations(io::Printer* printer) const { printer->Print(variables_, - "$type$ $name$(int index) const$deprecation$;\n" - "void set_$name$(int index, $type$ value)$deprecation$;\n" - "void add_$name$($type$ value)$deprecation$;\n"); + "$deprecated_attr$$type$ $name$(int index) const;\n" + "$deprecated_attr$void set_$name$(int index, $type$ value);\n" + "$deprecated_attr$void add_$name$($type$ value);\n"); printer->Print(variables_, - "const ::google::protobuf::RepeatedField< $type$ >&\n" - " $name$() const$deprecation$;\n" - "::google::protobuf::RepeatedField< $type$ >*\n" - " mutable_$name$()$deprecation$;\n"); + "$deprecated_attr$const ::google::protobuf::RepeatedField< $type$ >&\n" + " $name$() const;\n" + "$deprecated_attr$::google::protobuf::RepeatedField< $type$ >*\n" + " mutable_$name$();\n"); } void RepeatedPrimitiveFieldGenerator:: diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.h b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h index fcd7d684d..655ebde46 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h @@ -46,8 +46,8 @@ namespace cpp { class PrimitiveFieldGenerator : public FieldGenerator { public: - explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor, - const Options& options); + PrimitiveFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~PrimitiveFieldGenerator(); // implements FieldGenerator --------------------------------------- @@ -74,8 +74,8 @@ class PrimitiveFieldGenerator : public FieldGenerator { class PrimitiveOneofFieldGenerator : public PrimitiveFieldGenerator { public: - explicit PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor, - const Options& options); + PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~PrimitiveOneofFieldGenerator(); // implements FieldGenerator --------------------------------------- @@ -92,8 +92,8 @@ class PrimitiveOneofFieldGenerator : public PrimitiveFieldGenerator { class RepeatedPrimitiveFieldGenerator : public FieldGenerator { public: - explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, - const Options& options); + RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~RepeatedPrimitiveFieldGenerator(); // implements FieldGenerator --------------------------------------- diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc index 6b0821a6c..1d7434578 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -76,10 +76,9 @@ void SetStringVariables(const FieldDescriptor* descriptor, // =================================================================== -StringFieldGenerator:: -StringFieldGenerator(const FieldDescriptor* descriptor, - const Options& options) - : descriptor_(descriptor) { +StringFieldGenerator::StringFieldGenerator(const FieldDescriptor* descriptor, + const Options& options) + : FieldGenerator(options), descriptor_(descriptor) { SetStringVariables(descriptor, &variables_, options); } @@ -140,19 +139,19 @@ GenerateAccessorDeclarations(io::Printer* printer) const { } printer->Print(variables_, - "const ::std::string& $name$() const$deprecation$;\n" - "void set_$name$(const ::std::string& value)$deprecation$;\n" - "void set_$name$(const char* value)$deprecation$;\n" - "void set_$name$(const $pointer_type$* value, size_t size)" - "$deprecation$;\n" - "::std::string* mutable_$name$()$deprecation$;\n" - "::std::string* $release_name$()$deprecation$;\n" - "void set_allocated_$name$(::std::string* $name$)$deprecation$;\n"); + "$deprecated_attr$const ::std::string& $name$() const;\n" + "$deprecated_attr$void set_$name$(const ::std::string& value);\n" + "$deprecated_attr$void set_$name$(const char* value);\n" + "$deprecated_attr$void set_$name$(const $pointer_type$* value, size_t size)" + ";\n" + "$deprecated_attr$::std::string* mutable_$name$();\n" + "$deprecated_attr$::std::string* $release_name$();\n" + "$deprecated_attr$void set_allocated_$name$(::std::string* $name$);\n"); if (SupportsArenas(descriptor_)) { printer->Print(variables_, - "::std::string* unsafe_arena_release_$name$()$deprecation$;\n" - "void unsafe_arena_set_allocated_$name$(\n" - " ::std::string* $name$)$deprecation$;\n"); + "$deprecated_attr$::std::string* unsafe_arena_release_$name$();\n" + "$deprecated_attr$void unsafe_arena_set_allocated_$name$(\n" + " ::std::string* $name$);\n"); } @@ -199,10 +198,12 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " return $name$_.Mutable($default_variable$, GetArenaNoVirtual());\n" "}\n" "$inline$ ::std::string* $classname$::$release_name$() {\n" + " // @@protoc_insertion_point(field_release:$full_name$)\n" " $clear_hasbit$\n" " return $name$_.Release($default_variable$, GetArenaNoVirtual());\n" "}\n" "$inline$ ::std::string* $classname$::unsafe_arena_release_$name$() {\n" + " // @@protoc_insertion_point(field_unsafe_arena_release:$full_name$)\n" " GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);\n" " $clear_hasbit$\n" " return $name$_.UnsafeArenaRelease($default_variable$,\n" @@ -228,7 +229,8 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " }\n" " $name$_.UnsafeArenaSetAllocated($default_variable$,\n" " $name$, GetArenaNoVirtual());\n" - " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + " // @@protoc_insertion_point(field_unsafe_arena_set_allocated:" + "$full_name$)\n" "}\n"); } else { // No-arena case. @@ -261,6 +263,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " return $name$_.MutableNoArena($default_variable$);\n" "}\n" "$inline$ ::std::string* $classname$::$release_name$() {\n" + " // @@protoc_insertion_point(field_release:$full_name$)\n" " $clear_hasbit$\n" " return $name$_.ReleaseNoArena($default_variable$);\n" "}\n" @@ -369,7 +372,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { GenerateUtf8CheckCodeForString( - descriptor_, true, variables_, + descriptor_, options_, true, variables_, "this->$name$().data(), this->$name$().length(),\n", printer); } } @@ -378,7 +381,7 @@ void StringFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { GenerateUtf8CheckCodeForString( - descriptor_, false, variables_, + descriptor_, options_, false, variables_, "this->$name$().data(), this->$name$().length(),\n", printer); } printer->Print(variables_, @@ -390,7 +393,7 @@ void StringFieldGenerator:: GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { GenerateUtf8CheckCodeForString( - descriptor_, false, variables_, + descriptor_, options_, false, variables_, "this->$name$().data(), this->$name$().length(),\n", printer); } printer->Print(variables_, @@ -477,6 +480,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " // @@protoc_insertion_point(field_mutable:$full_name$)\n" "}\n" "$inline$ ::std::string* $classname$::$release_name$() {\n" + " // @@protoc_insertion_point(field_release:$full_name$)\n" " if (has_$name$()) {\n" " clear_has_$oneof_name$();\n" " return $oneof_prefix$$name$_.Release($default_variable$,\n" @@ -486,6 +490,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " }\n" "}\n" "$inline$ ::std::string* $classname$::unsafe_arena_release_$name$() {\n" + " // @@protoc_insertion_point(field_unsafe_arena_release:$full_name$)\n" " GOOGLE_DCHECK(GetArenaNoVirtual() != NULL);\n" " if (has_$name$()) {\n" " clear_has_$oneof_name$();\n" @@ -519,7 +524,8 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " $oneof_prefix$$name$_.UnsafeArenaSetAllocated($default_variable$, " "$name$, GetArenaNoVirtual());\n" " }\n" - " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + " // @@protoc_insertion_point(field_unsafe_arena_set_allocated:" + "$full_name$)\n" "}\n"); } else { // No-arena case. @@ -572,6 +578,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " return $oneof_prefix$$name$_.MutableNoArena($default_variable$);\n" "}\n" "$inline$ ::std::string* $classname$::$release_name$() {\n" + " // @@protoc_insertion_point(field_release:$full_name$)\n" " if (has_$name$()) {\n" " clear_has_$oneof_name$();\n" " return $oneof_prefix$$name$_.ReleaseNoArena($default_variable$);\n" @@ -658,7 +665,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { GenerateUtf8CheckCodeForString( - descriptor_, true, variables_, + descriptor_, options_, true, variables_, "this->$name$().data(), this->$name$().length(),\n", printer); } } @@ -666,10 +673,9 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { // =================================================================== -RepeatedStringFieldGenerator:: -RepeatedStringFieldGenerator(const FieldDescriptor* descriptor, - const Options& options) - : descriptor_(descriptor) { +RepeatedStringFieldGenerator::RepeatedStringFieldGenerator( + const FieldDescriptor* descriptor, const Options& options) + : FieldGenerator(options), descriptor_(descriptor) { SetStringVariables(descriptor, &variables_, options); } @@ -696,24 +702,24 @@ GenerateAccessorDeclarations(io::Printer* printer) const { } printer->Print(variables_, - "const ::std::string& $name$(int index) const$deprecation$;\n" - "::std::string* mutable_$name$(int index)$deprecation$;\n" - "void set_$name$(int index, const ::std::string& value)$deprecation$;\n" - "void set_$name$(int index, const char* value)$deprecation$;\n" + "$deprecated_attr$const ::std::string& $name$(int index) const;\n" + "$deprecated_attr$::std::string* mutable_$name$(int index);\n" + "$deprecated_attr$void set_$name$(int index, const ::std::string& value);\n" + "$deprecated_attr$void set_$name$(int index, const char* value);\n" "" - "void set_$name$(int index, const $pointer_type$* value, size_t size)" - "$deprecation$;\n" - "::std::string* add_$name$()$deprecation$;\n" - "void add_$name$(const ::std::string& value)$deprecation$;\n" - "void add_$name$(const char* value)$deprecation$;\n" - "void add_$name$(const $pointer_type$* value, size_t size)" - "$deprecation$;\n"); + "$deprecated_attr$void set_$name$(" + "int index, const $pointer_type$* value, size_t size);\n" + "$deprecated_attr$::std::string* add_$name$();\n" + "$deprecated_attr$void add_$name$(const ::std::string& value);\n" + "$deprecated_attr$void add_$name$(const char* value);\n" + "$deprecated_attr$void add_$name$(const $pointer_type$* value, size_t size)" + ";\n"); printer->Print(variables_, - "const ::google::protobuf::RepeatedPtrField< ::std::string>& $name$() const" - "$deprecation$;\n" - "::google::protobuf::RepeatedPtrField< ::std::string>* mutable_$name$()" - "$deprecation$;\n"); + "$deprecated_attr$const ::google::protobuf::RepeatedPtrField< ::std::string>& $name$() " + "const;\n" + "$deprecated_attr$::google::protobuf::RepeatedPtrField< ::std::string>* mutable_$name$()" + ";\n"); if (unknown_ctype) { printer->Outdent(); @@ -752,6 +758,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " // @@protoc_insertion_point(field_set_pointer:$full_name$)\n" "}\n" "$inline$ ::std::string* $classname$::add_$name$() {\n" + " // @@protoc_insertion_point(field_add_mutable:$full_name$)\n" " return $name$_.Add();\n" "}\n" "$inline$ void $classname$::add_$name$(const ::std::string& value) {\n" @@ -807,7 +814,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { " input, this->add_$name$()));\n"); if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { GenerateUtf8CheckCodeForString( - descriptor_, true, variables_, + descriptor_, options_, true, variables_, "this->$name$(this->$name$_size() - 1).data(),\n" "this->$name$(this->$name$_size() - 1).length(),\n", printer); @@ -821,7 +828,7 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { printer->Indent(); if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { GenerateUtf8CheckCodeForString( - descriptor_, false, variables_, + descriptor_, options_, false, variables_, "this->$name$(i).data(), this->$name$(i).length(),\n", printer); } printer->Outdent(); @@ -838,7 +845,7 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { printer->Indent(); if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { GenerateUtf8CheckCodeForString( - descriptor_, false, variables_, + descriptor_, options_, false, variables_, "this->$name$(i).data(), this->$name$(i).length(),\n", printer); } printer->Outdent(); diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.h b/src/google/protobuf/compiler/cpp/cpp_string_field.h index 616e20674..cb4e8772d 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.h @@ -46,8 +46,8 @@ namespace cpp { class StringFieldGenerator : public FieldGenerator { public: - explicit StringFieldGenerator(const FieldDescriptor* descriptor, - const Options& options); + StringFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~StringFieldGenerator(); // implements FieldGenerator --------------------------------------- @@ -79,8 +79,8 @@ class StringFieldGenerator : public FieldGenerator { class StringOneofFieldGenerator : public StringFieldGenerator { public: - explicit StringOneofFieldGenerator(const FieldDescriptor* descriptor, - const Options& options); + StringOneofFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~StringOneofFieldGenerator(); // implements FieldGenerator --------------------------------------- @@ -99,8 +99,8 @@ class StringOneofFieldGenerator : public StringFieldGenerator { class RepeatedStringFieldGenerator : public FieldGenerator { public: - explicit RepeatedStringFieldGenerator(const FieldDescriptor* descriptor, - const Options& options); + RepeatedStringFieldGenerator(const FieldDescriptor* descriptor, + const Options& options); ~RepeatedStringFieldGenerator(); // implements FieldGenerator --------------------------------------- diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc index 9942a343a..5d82946d6 100644 --- a/src/google/protobuf/compiler/cpp/cpp_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -82,7 +82,6 @@ namespace google { namespace protobuf { -using internal::NewPermanentCallback; namespace compiler { namespace cpp { @@ -1253,7 +1252,7 @@ class GeneratedServiceTest : public testing::Test { foo_(descriptor_->FindMethodByName("Foo")), bar_(descriptor_->FindMethodByName("Bar")), stub_(&mock_channel_), - done_(NewPermanentCallback(&DoNothing)) {} + done_(::google::protobuf::internal::NewPermanentCallback(&DoNothing)) {} virtual void SetUp() { ASSERT_TRUE(foo_ != NULL); diff --git a/src/google/protobuf/compiler/cpp/metadata_test.cc b/src/google/protobuf/compiler/cpp/metadata_test.cc index 422eb73b1..edd30780a 100644 --- a/src/google/protobuf/compiler/cpp/metadata_test.cc +++ b/src/google/protobuf/compiler/cpp/metadata_test.cc @@ -41,9 +41,10 @@ #include #include +#include +#include #include #include -#include namespace google { namespace protobuf { @@ -51,6 +52,189 @@ namespace compiler { namespace cpp { namespace { +// A CodeGenerator that captures the FileDescriptor it's passed as a +// FileDescriptorProto. +class DescriptorCapturingGenerator : public CodeGenerator { + public: + // Does not own file; file must outlive the Generator. + explicit DescriptorCapturingGenerator(FileDescriptorProto* file) + : file_(file) {} + + virtual bool Generate(const FileDescriptor* file, const string& parameter, + GeneratorContext* context, string* error) const { + file->CopyTo(file_); + return true; + } + + private: + FileDescriptorProto* file_; +}; + +class CppMetadataTest : public ::testing::Test { + public: + // Adds a file with name `filename` and content `data`. + void AddFile(const string& filename, const string& data) { + GOOGLE_CHECK_OK(File::SetContents(TestTempDir() + "/" + filename, data, + true)); + } + + // Tries to capture a FileDescriptorProto, GeneratedCodeInfo, and output + // code from the previously added file with name `filename`. Returns true on + // success. If pb_h is non-null, expects a .pb.h and a .pb.h.meta (copied to + // pb_h and pb_h_info respecfively); similarly for proto_h and proto_h_info. + bool CaptureMetadata(const string& filename, FileDescriptorProto* file, + string* pb_h, GeneratedCodeInfo* pb_h_info, + string* proto_h, GeneratedCodeInfo* proto_h_info, + string* pb_cc) { + google::protobuf::compiler::CommandLineInterface cli; + cli.SetInputsAreProtoPathRelative(true); + + CppGenerator cpp_generator; + DescriptorCapturingGenerator capturing_generator(file); + cli.RegisterGenerator("--cpp_out", &cpp_generator, ""); + cli.RegisterGenerator("--capture_out", &capturing_generator, ""); + + string proto_path = "-I" + TestTempDir(); + string cpp_out = + "--cpp_out=annotate_headers=true," + "annotation_pragma_name=pragma_name," + "annotation_guard_name=guard_name:" + + TestTempDir(); + string capture_out = "--capture_out=" + TestTempDir(); + + const char* argv[] = {"protoc", proto_path.c_str(), cpp_out.c_str(), + capture_out.c_str(), filename.c_str()}; + + if (cli.Run(5, argv) != 0) { + return false; + } + + string output_base = TestTempDir() + "/" + StripProto(filename); + + if (pb_cc != NULL) { + GOOGLE_CHECK_OK( + File::GetContents(output_base + ".pb.cc", pb_cc, true)); + } + + if (pb_h != NULL && pb_h_info != NULL) { + GOOGLE_CHECK_OK( + File::GetContents(output_base + ".pb.h", pb_h, true)); + if (!DecodeMetadata(output_base + ".pb.h.meta", pb_h_info)) { + return false; + } + } + + if (proto_h != NULL && proto_h_info != NULL) { + GOOGLE_CHECK_OK(File::GetContents(output_base + ".proto.h", proto_h, + true)); + if (!DecodeMetadata(output_base + ".proto.h.meta", proto_h_info)) { + return false; + } + } + + return true; + } + + private: + // Decodes GeneratedCodeInfo stored in path and copies it to info. + // Returns true on success. + bool DecodeMetadata(const string& path, GeneratedCodeInfo* info) { + string data; + GOOGLE_CHECK_OK(File::GetContents(path, &data, true)); + io::ArrayInputStream input(data.data(), data.size()); + return info->ParseFromZeroCopyStream(&input); + } +}; + +const char kSmallTestFile[] = + "syntax = \"proto2\";\n" + "package foo;\n" + "enum Enum { VALUE = 0; }\n" + "message Message { }\n"; + +// Finds the Annotation for a given source file and path (or returns null if it +// couldn't). +const GeneratedCodeInfo::Annotation* FindAnnotationOnPath( + const GeneratedCodeInfo& info, const string& source_file, + const vector& path) { + for (int i = 0; i < info.annotation_size(); ++i) { + const GeneratedCodeInfo::Annotation* annotation = &info.annotation(i); + if (annotation->source_file() != source_file || + annotation->path_size() != path.size()) { + continue; + } + int node = 0; + for (; node < path.size(); ++node) { + if (annotation->path(node) != path[node]) { + break; + } + } + if (node == path.size()) { + return annotation; + } + } + return NULL; +} + +// Returns true if the provided annotation covers a given substring in +// file_content. +bool AnnotationMatchesSubstring(const string& file_content, + const GeneratedCodeInfo::Annotation* annotation, + const string& expected_text) { + uint32 begin = annotation->begin(); + uint32 end = annotation->end(); + if (end < begin || end > file_content.size()) { + return false; + } + return file_content.substr(begin, end - begin) == expected_text; +} + +TEST_F(CppMetadataTest, CapturesEnumNames) { + FileDescriptorProto file; + GeneratedCodeInfo info; + string pb_h; + AddFile("test.proto", kSmallTestFile); + EXPECT_TRUE( + CaptureMetadata("test.proto", &file, &pb_h, &info, NULL, NULL, NULL)); + EXPECT_EQ("Enum", file.enum_type(0).name()); + vector enum_path; + enum_path.push_back(FileDescriptorProto::kEnumTypeFieldNumber); + enum_path.push_back(0); + const GeneratedCodeInfo::Annotation* enum_annotation = + FindAnnotationOnPath(info, "test.proto", enum_path); + EXPECT_TRUE(NULL != enum_annotation); + EXPECT_TRUE(AnnotationMatchesSubstring(pb_h, enum_annotation, "Enum")); +} + +TEST_F(CppMetadataTest, AddsPragma) { + FileDescriptorProto file; + GeneratedCodeInfo info; + string pb_h; + AddFile("test.proto", kSmallTestFile); + EXPECT_TRUE( + CaptureMetadata("test.proto", &file, &pb_h, &info, NULL, NULL, NULL)); + EXPECT_TRUE(pb_h.find("#ifdef guard_name") != string::npos); + EXPECT_TRUE(pb_h.find("#pragma pragma_name \"test.pb.h.meta\"") != + string::npos); +} + +TEST_F(CppMetadataTest, CapturesMessageNames) { + FileDescriptorProto file; + GeneratedCodeInfo info; + string pb_h; + AddFile("test.proto", kSmallTestFile); + EXPECT_TRUE( + CaptureMetadata("test.proto", &file, &pb_h, &info, NULL, NULL, NULL)); + EXPECT_EQ("Message", file.message_type(0).name()); + vector message_path; + message_path.push_back(FileDescriptorProto::kMessageTypeFieldNumber); + message_path.push_back(0); + const GeneratedCodeInfo::Annotation* message_annotation = + FindAnnotationOnPath(info, "test.proto", message_path); + EXPECT_TRUE(NULL != message_annotation); + EXPECT_TRUE(AnnotationMatchesSubstring(pb_h, message_annotation, "Message")); +} + } // namespace } // namespace cpp } // namespace compiler diff --git a/src/google/protobuf/compiler/csharp/csharp_enum.cc b/src/google/protobuf/compiler/csharp/csharp_enum.cc index 566819898..bdfcc2bee 100644 --- a/src/google/protobuf/compiler/csharp/csharp_enum.cc +++ b/src/google/protobuf/compiler/csharp/csharp_enum.cc @@ -41,6 +41,7 @@ #include #include #include +#include using google::protobuf::internal::scoped_ptr; @@ -49,8 +50,8 @@ namespace protobuf { namespace compiler { namespace csharp { -EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor) : - SourceGeneratorBase(descriptor->file()), +EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, const Options* options) : + SourceGeneratorBase(descriptor->file(), options), descriptor_(descriptor) { } @@ -64,11 +65,24 @@ void EnumGenerator::Generate(io::Printer* printer) { "access_level", class_access_level(), "name", descriptor_->name()); printer->Indent(); + std::set used_names; for (int i = 0; i < descriptor_->value_count(); i++) { WriteEnumValueDocComment(printer, descriptor_->value(i)); - printer->Print("$name$ = $number$,\n", - "name", descriptor_->value(i)->name(), - "number", SimpleItoa(descriptor_->value(i)->number())); + string original_name = descriptor_->value(i)->name(); + string name = options()->legacy_enum_values + ? descriptor_->value(i)->name() + : GetEnumValueName(descriptor_->name(), descriptor_->value(i)->name()); + // Make sure we don't get any duplicate names due to prefix removal. + while (!used_names.insert(name).second) { + // It's possible we'll end up giving this warning multiple times, but that's better than not at all. + GOOGLE_LOG(WARNING) << "Duplicate enum value " << name << " (originally " << original_name + << ") in " << descriptor_->name() << "; adding underscore to distinguish"; + name += "_"; + } + printer->Print("[pbr::OriginalName(\"$original_name$\")] $name$ = $number$,\n", + "original_name", original_name, + "name", name, + "number", SimpleItoa(descriptor_->value(i)->number())); } printer->Outdent(); printer->Print("}\n"); diff --git a/src/google/protobuf/compiler/csharp/csharp_enum.h b/src/google/protobuf/compiler/csharp/csharp_enum.h index 2cf2fad40..8925cdf2b 100644 --- a/src/google/protobuf/compiler/csharp/csharp_enum.h +++ b/src/google/protobuf/compiler/csharp/csharp_enum.h @@ -43,7 +43,7 @@ namespace csharp { class EnumGenerator : public SourceGeneratorBase { public: - EnumGenerator(const EnumDescriptor* descriptor); + EnumGenerator(const EnumDescriptor* descriptor, const Options* options); ~EnumGenerator(); void Generate(io::Printer* printer); diff --git a/src/google/protobuf/compiler/csharp/csharp_enum_field.cc b/src/google/protobuf/compiler/csharp/csharp_enum_field.cc index d38fb1ed2..67c0b5961 100644 --- a/src/google/protobuf/compiler/csharp/csharp_enum_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_enum_field.cc @@ -38,6 +38,7 @@ #include #include +#include #include namespace google { @@ -46,8 +47,8 @@ namespace compiler { namespace csharp { EnumFieldGenerator::EnumFieldGenerator(const FieldDescriptor* descriptor, - int fieldOrdinal) - : PrimitiveFieldGenerator(descriptor, fieldOrdinal) { + int fieldOrdinal, const Options *options) + : PrimitiveFieldGenerator(descriptor, fieldOrdinal, options) { } EnumFieldGenerator::~EnumFieldGenerator() { @@ -80,9 +81,9 @@ void EnumFieldGenerator::GenerateCodecCode(io::Printer* printer) { "pb::FieldCodec.ForEnum($tag$, x => (int) x, x => ($type_name$) x)"); } -EnumOneofFieldGenerator::EnumOneofFieldGenerator(const FieldDescriptor* descriptor, - int fieldOrdinal) - : PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal) { +EnumOneofFieldGenerator::EnumOneofFieldGenerator( + const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options) + : PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal, options) { } EnumOneofFieldGenerator::~EnumOneofFieldGenerator() { diff --git a/src/google/protobuf/compiler/csharp/csharp_enum_field.h b/src/google/protobuf/compiler/csharp/csharp_enum_field.h index 083641578..9b7669ba5 100644 --- a/src/google/protobuf/compiler/csharp/csharp_enum_field.h +++ b/src/google/protobuf/compiler/csharp/csharp_enum_field.h @@ -43,7 +43,9 @@ namespace csharp { class EnumFieldGenerator : public PrimitiveFieldGenerator { public: - EnumFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + EnumFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options *options); ~EnumFieldGenerator(); virtual void GenerateCodecCode(io::Printer* printer); @@ -57,7 +59,9 @@ class EnumFieldGenerator : public PrimitiveFieldGenerator { class EnumOneofFieldGenerator : public PrimitiveOneofFieldGenerator { public: - EnumOneofFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + EnumOneofFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options *options); ~EnumOneofFieldGenerator(); virtual void GenerateParsingCode(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 5df43d3f9..e3c34040b 100644 --- a/src/google/protobuf/compiler/csharp/csharp_field_base.cc +++ b/src/google/protobuf/compiler/csharp/csharp_field_base.cc @@ -94,14 +94,15 @@ void FieldGeneratorBase::SetCommonFieldVariables( void FieldGeneratorBase::SetCommonOneofFieldVariables( map* variables) { (*variables)["oneof_name"] = oneof_name(); - (*variables)["has_property_check"] = oneof_name() + "Case_ == " + oneof_property_name() + + (*variables)["has_property_check"] = + oneof_name() + "Case_ == " + oneof_property_name() + "OneofCase." + property_name(); (*variables)["oneof_property_name"] = oneof_property_name(); } FieldGeneratorBase::FieldGeneratorBase(const FieldDescriptor* descriptor, - int fieldOrdinal) - : SourceGeneratorBase(descriptor->file()), + int fieldOrdinal, const Options* options) + : SourceGeneratorBase(descriptor->file(), options), descriptor_(descriptor), fieldOrdinal_(fieldOrdinal) { SetCommonFieldVariables(&variables_); @@ -158,10 +159,11 @@ std::string FieldGeneratorBase::type_name(const FieldDescriptor* descriptor) { case FieldDescriptor::TYPE_MESSAGE: case FieldDescriptor::TYPE_GROUP: if (IsWrapperType(descriptor)) { - const FieldDescriptor* wrapped_field = descriptor->message_type()->field(0); + const FieldDescriptor* wrapped_field = + descriptor->message_type()->field(0); string wrapped_field_type_name = type_name(wrapped_field); - // String and ByteString go to the same type; other wrapped types go to the - // nullable equivalent. + // String and ByteString go to the same type; other wrapped types + // go to the nullable equivalent. if (wrapped_field->type() == FieldDescriptor::TYPE_STRING || wrapped_field->type() == FieldDescriptor::TYPE_BYTES) { return wrapped_field_type_name; @@ -304,7 +306,9 @@ std::string FieldGeneratorBase::default_value() { std::string FieldGeneratorBase::default_value(const FieldDescriptor* descriptor) { switch (descriptor->type()) { case FieldDescriptor::TYPE_ENUM: - return type_name() + "." + descriptor->default_value_enum()->name(); + // All proto3 enums have a default value of 0, and there's an implicit conversion from the constant 0 to + // any C# enum. This means we don't need to work out what we actually mapped the enum value name to. + return "0"; case FieldDescriptor::TYPE_MESSAGE: case FieldDescriptor::TYPE_GROUP: if (IsWrapperType(descriptor)) { diff --git a/src/google/protobuf/compiler/csharp/csharp_field_base.h b/src/google/protobuf/compiler/csharp/csharp_field_base.h index d83543bd2..4109f3caa 100644 --- a/src/google/protobuf/compiler/csharp/csharp_field_base.h +++ b/src/google/protobuf/compiler/csharp/csharp_field_base.h @@ -44,7 +44,9 @@ namespace csharp { class FieldGeneratorBase : public SourceGeneratorBase { public: - FieldGeneratorBase(const FieldDescriptor* descriptor, int fieldOrdinal); + FieldGeneratorBase(const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options* options); ~FieldGeneratorBase(); virtual void GenerateCloningCode(io::Printer* printer) = 0; diff --git a/src/google/protobuf/compiler/csharp/csharp_generator.cc b/src/google/protobuf/compiler/csharp/csharp_generator.cc index 825de542c..d74e8c884 100644 --- a/src/google/protobuf/compiler/csharp/csharp_generator.cc +++ b/src/google/protobuf/compiler/csharp/csharp_generator.cc @@ -41,6 +41,7 @@ #include #include #include +#include #include using google::protobuf::internal::scoped_ptr; @@ -51,8 +52,9 @@ namespace compiler { namespace csharp { void GenerateFile(const google::protobuf::FileDescriptor* file, - io::Printer* printer) { - ReflectionClassGenerator reflectionClassGenerator(file); + io::Printer* printer, + const Options* options) { + ReflectionClassGenerator reflectionClassGenerator(file, options); reflectionClassGenerator.Generate(printer); } @@ -71,15 +73,19 @@ bool Generator::Generate( return false; } - std::string file_extension = ".cs"; - std::string base_namespace = ""; - bool generate_directories = false; + struct Options cli_options; + for (int i = 0; i < options.size(); i++) { if (options[i].first == "file_extension") { - file_extension = options[i].second; + cli_options.file_extension = options[i].second; } else if (options[i].first == "base_namespace") { - base_namespace = options[i].second; - generate_directories = true; + cli_options.base_namespace = options[i].second; + cli_options.base_namespace_specified = true; + } else if (options[i].first == "internal_access") { + cli_options.internal_access = true; + } else if (options[i].first == "legacy_enum_values") { + // TODO: Remove this before final release + cli_options.legacy_enum_values = true; } else { *error = "Unknown generator option: " + options[i].first; return false; @@ -87,7 +93,12 @@ bool Generator::Generate( } string filename_error = ""; - std::string filename = GetOutputFile(file, file_extension, generate_directories, base_namespace, &filename_error); + std::string filename = GetOutputFile(file, + cli_options.file_extension, + cli_options.base_namespace_specified, + cli_options.base_namespace, + &filename_error); + if (filename.empty()) { *error = filename_error; return false; @@ -96,7 +107,7 @@ bool Generator::Generate( generator_context->Open(filename)); io::Printer printer(output.get(), '$'); - GenerateFile(file, &printer); + GenerateFile(file, &printer, &cli_options); return true; } diff --git a/src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc b/src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc index 7ef7df42b..5755fee00 100644 --- a/src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc +++ b/src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc @@ -30,8 +30,8 @@ #include -#include #include +#include #include #include @@ -45,7 +45,23 @@ namespace compiler { namespace csharp { namespace { -// TODO(jtattermusch): add some tests. +TEST(CSharpEnumValue, PascalCasedPrefixStripping) { + EXPECT_EQ("Bar", GetEnumValueName("Foo", "BAR")); + EXPECT_EQ("BarBaz", GetEnumValueName("Foo", "BAR_BAZ")); + EXPECT_EQ("Bar", GetEnumValueName("Foo", "FOO_BAR")); + EXPECT_EQ("Bar", GetEnumValueName("Foo", "FOO__BAR")); + EXPECT_EQ("BarBaz", GetEnumValueName("Foo", "FOO_BAR_BAZ")); + EXPECT_EQ("BarBaz", GetEnumValueName("Foo", "Foo_BarBaz")); + EXPECT_EQ("Bar", GetEnumValueName("FO_O", "FOO_BAR")); + EXPECT_EQ("Bar", GetEnumValueName("FOO", "F_O_O_BAR")); + EXPECT_EQ("Bar", GetEnumValueName("Foo", "BAR")); + EXPECT_EQ("BarBaz", GetEnumValueName("Foo", "BAR_BAZ")); + EXPECT_EQ("Foo", GetEnumValueName("Foo", "FOO")); + EXPECT_EQ("Foo", GetEnumValueName("Foo", "FOO___")); + // Identifiers can't start with digits + EXPECT_EQ("_2Bar", GetEnumValueName("Foo", "FOO_2_BAR")); + EXPECT_EQ("_2", GetEnumValueName("Foo", "FOO___2")); +} } // namespace } // namespace csharp diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.cc b/src/google/protobuf/compiler/csharp/csharp_helpers.cc index c51fe44b3..6c154c5a5 100644 --- a/src/google/protobuf/compiler/csharp/csharp_helpers.cc +++ b/src/google/protobuf/compiler/csharp/csharp_helpers.cc @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -127,7 +128,8 @@ std::string GetFileNameBase(const FileDescriptor* descriptor) { } std::string GetReflectionClassUnqualifiedName(const FileDescriptor* descriptor) { - // TODO: Detect collisions with existing messages, and append an underscore if necessary. + // TODO: Detect collisions with existing messages, + // and append an underscore if necessary. return GetFileNameBase(descriptor) + "Reflection"; } @@ -176,6 +178,104 @@ std::string UnderscoresToPascalCase(const std::string& input) { return UnderscoresToCamelCase(input, true); } +// Convert a string which is expected to be SHOUTY_CASE (but may not be *precisely* shouty) +// into a PascalCase string. Precise rules implemented: + +// Previous input character Current character Case +// Any Non-alphanumeric Skipped +// None - first char of input Alphanumeric Upper +// Non-letter (e.g. _ or 1) Alphanumeric Upper +// Numeric Alphanumeric Upper +// Lower letter Alphanumeric Same as current +// Upper letter Alphanumeric Lower +std::string ShoutyToPascalCase(const std::string& input) { + string result; + // Simple way of implementing "always start with upper" + char previous = '_'; + for (int i = 0; i < input.size(); i++) { + char current = input[i]; + if (!ascii_isalnum(current)) { + previous = current; + continue; + } + if (!ascii_isalnum(previous)) { + result += ascii_toupper(current); + } else if (ascii_isdigit(previous)) { + result += ascii_toupper(current); + } else if (ascii_islower(previous)) { + result += current; + } else { + result += ascii_tolower(current); + } + previous = current; + } + return result; +} + +// Attempt to remove a prefix from a value, ignoring casing and skipping underscores. +// (foo, foo_bar) => bar - underscore after prefix is skipped +// (FOO, foo_bar) => bar - casing is ignored +// (foo_bar, foobarbaz) => baz - underscore in prefix is ignored +// (foobar, foo_barbaz) => baz - underscore in value is ignored +// (foo, bar) => bar - prefix isn't matched; return original value +std::string TryRemovePrefix(const std::string& prefix, const std::string& value) { + // First normalize to a lower-case no-underscores prefix to match against + std::string prefix_to_match = ""; + for (size_t i = 0; i < prefix.size(); i++) { + if (prefix[i] != '_') { + prefix_to_match += ascii_tolower(prefix[i]); + } + } + + // This keeps track of how much of value we've consumed + size_t prefix_index, value_index; + for (prefix_index = 0, value_index = 0; + prefix_index < prefix_to_match.size() && value_index < value.size(); + value_index++) { + // Skip over underscores in the value + if (value[value_index] == '_') { + continue; + } + if (ascii_tolower(value[value_index]) != prefix_to_match[prefix_index++]) { + // Failed to match the prefix - bail out early. + return value; + } + } + + // If we didn't finish looking through the prefix, we can't strip it. + if (prefix_index < prefix_to_match.size()) { + return value; + } + + // Step over any underscores after the prefix + while (value_index < value.size() && value[value_index] == '_') { + value_index++; + } + + // If there's nothing left (e.g. it was a prefix with only underscores afterwards), don't strip. + if (value_index == value.size()) { + return value; + } + + return value.substr(value_index); +} + +// Format the enum value name in a pleasant way for C#: +// - Strip the enum name as a prefix if possible +// - Convert to PascalCase. +// For example, an enum called Color with a value of COLOR_BLUE should +// result in an enum value in C# called just Blue +std::string GetEnumValueName(const std::string& enum_name, const std::string& enum_value_name) { + std::string stripped = TryRemovePrefix(enum_name, enum_value_name); + std::string result = ShoutyToPascalCase(stripped); + // Just in case we have an enum name of FOO and a value of FOO_2... make sure the returned + // string is a valid identifier. + if (ascii_isdigit(result[0])) { + result = "_" + result; + } + return result; +} + std::string ToCSharpName(const std::string& name, const FileDescriptor* file) { std::string result = GetFileNamespace(file); if (result != "") { @@ -351,49 +451,50 @@ std::string FileDescriptorToBase64(const FileDescriptor* descriptor) { } FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, - int fieldOrdinal) { + int fieldOrdinal, + const Options* options) { switch (descriptor->type()) { case FieldDescriptor::TYPE_GROUP: case FieldDescriptor::TYPE_MESSAGE: if (descriptor->is_repeated()) { if (descriptor->is_map()) { - return new MapFieldGenerator(descriptor, fieldOrdinal); + return new MapFieldGenerator(descriptor, fieldOrdinal, options); } else { - return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal); + return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal, options); } } else { if (IsWrapperType(descriptor)) { if (descriptor->containing_oneof()) { - return new WrapperOneofFieldGenerator(descriptor, fieldOrdinal); + return new WrapperOneofFieldGenerator(descriptor, fieldOrdinal, options); } else { - return new WrapperFieldGenerator(descriptor, fieldOrdinal); + return new WrapperFieldGenerator(descriptor, fieldOrdinal, options); } } else { if (descriptor->containing_oneof()) { - return new MessageOneofFieldGenerator(descriptor, fieldOrdinal); + return new MessageOneofFieldGenerator(descriptor, fieldOrdinal, options); } else { - return new MessageFieldGenerator(descriptor, fieldOrdinal); + return new MessageFieldGenerator(descriptor, fieldOrdinal, options); } } } case FieldDescriptor::TYPE_ENUM: if (descriptor->is_repeated()) { - return new RepeatedEnumFieldGenerator(descriptor, fieldOrdinal); + return new RepeatedEnumFieldGenerator(descriptor, fieldOrdinal, options); } else { if (descriptor->containing_oneof()) { - return new EnumOneofFieldGenerator(descriptor, fieldOrdinal); + return new EnumOneofFieldGenerator(descriptor, fieldOrdinal, options); } else { - return new EnumFieldGenerator(descriptor, fieldOrdinal); + return new EnumFieldGenerator(descriptor, fieldOrdinal, options); } } default: if (descriptor->is_repeated()) { - return new RepeatedPrimitiveFieldGenerator(descriptor, fieldOrdinal); + return new RepeatedPrimitiveFieldGenerator(descriptor, fieldOrdinal, options); } else { if (descriptor->containing_oneof()) { - return new PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal); + return new PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal, options); } else { - return new PrimitiveFieldGenerator(descriptor, fieldOrdinal); + return new PrimitiveFieldGenerator(descriptor, fieldOrdinal, options); } } } diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.h b/src/google/protobuf/compiler/csharp/csharp_helpers.h index e96e79388..1563ca7e1 100644 --- a/src/google/protobuf/compiler/csharp/csharp_helpers.h +++ b/src/google/protobuf/compiler/csharp/csharp_helpers.h @@ -36,6 +36,7 @@ #define GOOGLE_PROTOBUF_COMPILER_CSHARP_HELPERS_H__ #include +#include #include #include #include @@ -46,6 +47,7 @@ namespace protobuf { namespace compiler { namespace csharp { +struct Options; class FieldGeneratorBase; // TODO: start using this enum. @@ -82,7 +84,9 @@ std::string GetPropertyName(const FieldDescriptor* descriptor); int GetFixedSize(FieldDescriptor::Type type); -std::string UnderscoresToCamelCase(const std::string& input, bool cap_next_letter, bool preserve_period); +std::string UnderscoresToCamelCase(const std::string& input, + bool cap_next_letter, + bool preserve_period); inline std::string UnderscoresToCamelCase(const std::string& input, bool cap_next_letter) { return UnderscoresToCamelCase(input, cap_next_letter, false); @@ -90,22 +94,28 @@ inline std::string UnderscoresToCamelCase(const std::string& input, bool cap_nex std::string UnderscoresToPascalCase(const std::string& input); +// Note that we wouldn't normally want to export this (we're not expecting +// it to be used outside libprotoc itself) but this exposes it for testing. +std::string LIBPROTOBUF_EXPORT GetEnumValueName(const std::string& enum_name, const std::string& enum_value_name); + // TODO(jtattermusch): perhaps we could move this to strutil std::string StringToBase64(const std::string& input); std::string FileDescriptorToBase64(const FileDescriptor* descriptor); -FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); +FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options* options); -// Determines whether the given message is a map entry message, i.e. one implicitly created -// by protoc due to a map field. +// Determines whether the given message is a map entry message, +// i.e. one implicitly created by protoc due to a map field. inline bool IsMapEntryMessage(const Descriptor* descriptor) { return descriptor->options().map_entry(); } -// Determines whether we're generating code for the proto representation of descriptors etc, -// for use in the runtime. This is the only type which is allowed to use proto2 syntax, -// and it generates internal classes. +// Determines whether we're generating code for the proto representation of +// descriptors etc, for use in the runtime. This is the only type which is +// allowed to use proto2 syntax, and it generates internal classes. inline bool IsDescriptorProto(const FileDescriptor* descriptor) { return descriptor->name() == "google/protobuf/descriptor.proto"; } diff --git a/src/google/protobuf/compiler/csharp/csharp_map_field.cc b/src/google/protobuf/compiler/csharp/csharp_map_field.cc index 15c68b3f5..565d12259 100644 --- a/src/google/protobuf/compiler/csharp/csharp_map_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_map_field.cc @@ -48,8 +48,9 @@ namespace compiler { namespace csharp { MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor, - int fieldOrdinal) - : FieldGeneratorBase(descriptor, fieldOrdinal) { + int fieldOrdinal, + const Options* options) + : FieldGeneratorBase(descriptor, fieldOrdinal, options) { } MapFieldGenerator::~MapFieldGenerator() { @@ -62,8 +63,10 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) { descriptor_->message_type()->FindFieldByName("value"); variables_["key_type_name"] = type_name(key_descriptor); variables_["value_type_name"] = type_name(value_descriptor); - scoped_ptr key_generator(CreateFieldGenerator(key_descriptor, 1)); - scoped_ptr value_generator(CreateFieldGenerator(value_descriptor, 2)); + scoped_ptr key_generator( + CreateFieldGenerator(key_descriptor, 1, this->options())); + scoped_ptr value_generator( + CreateFieldGenerator(value_descriptor, 2, this->options())); printer->Print( variables_, diff --git a/src/google/protobuf/compiler/csharp/csharp_map_field.h b/src/google/protobuf/compiler/csharp/csharp_map_field.h index f33fe1c39..84a33a036 100644 --- a/src/google/protobuf/compiler/csharp/csharp_map_field.h +++ b/src/google/protobuf/compiler/csharp/csharp_map_field.h @@ -43,7 +43,9 @@ namespace csharp { class MapFieldGenerator : public FieldGeneratorBase { public: - MapFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + MapFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options* options); ~MapFieldGenerator(); virtual void GenerateCloningCode(io::Printer* printer); diff --git a/src/google/protobuf/compiler/csharp/csharp_message.cc b/src/google/protobuf/compiler/csharp/csharp_message.cc index e0230a244..532da6b9e 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message.cc +++ b/src/google/protobuf/compiler/csharp/csharp_message.cc @@ -60,8 +60,9 @@ bool CompareFieldNumbers(const FieldDescriptor* d1, const FieldDescriptor* d2) { return d1->number() < d2->number(); } -MessageGenerator::MessageGenerator(const Descriptor* descriptor) - : SourceGeneratorBase(descriptor->file()), +MessageGenerator::MessageGenerator(const Descriptor* descriptor, + const Options* options) + : SourceGeneratorBase(descriptor->file(), options), descriptor_(descriptor) { // sorted field names @@ -185,7 +186,8 @@ void MessageGenerator::Generate(io::Printer* printer) { } printer->Outdent(); printer->Print("}\n"); - // TODO: Should we put the oneof .proto comments here? It's unclear exactly where they should go. + // TODO: Should we put the oneof .proto comments here? + // It's unclear exactly where they should go. printer->Print( vars, "private $property_name$OneofCase $name$Case_ = $property_name$OneofCase.None;\n" @@ -214,13 +216,14 @@ void MessageGenerator::Generate(io::Printer* printer) { printer->Print("public static partial class Types {\n"); printer->Indent(); for (int i = 0; i < descriptor_->enum_type_count(); i++) { - EnumGenerator enumGenerator(descriptor_->enum_type(i)); + EnumGenerator enumGenerator(descriptor_->enum_type(i), this->options()); enumGenerator.Generate(printer); } for (int i = 0; i < descriptor_->nested_type_count(); i++) { // Don't generate nested types for maps... if (!IsMapEntryMessage(descriptor_->nested_type(i))) { - MessageGenerator messageGenerator(descriptor_->nested_type(i)); + MessageGenerator messageGenerator( + descriptor_->nested_type(i), this->options()); messageGenerator.Generate(printer); } } @@ -268,7 +271,8 @@ void MessageGenerator::GenerateCloningCode(io::Printer* printer) { // Clone just the right field for each oneof for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) { vars["name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false); - vars["property_name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true); + vars["property_name"] = UnderscoresToCamelCase( + descriptor_->oneof_decl(i)->name(), true); printer->Print(vars, "switch (other.$property_name$Case) {\n"); printer->Indent(); for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { @@ -449,7 +453,8 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) { uint32 tag = internal::WireFormatLite::MakeTag(field->number(), wt); // Handle both packed and unpacked repeated fields with the same Read*Array call; // the two generated cases are the packed and unpacked tags. - // TODO(jonskeet): Check that is_packable is equivalent to is_repeated && wt in { VARINT, FIXED32, FIXED64 }. + // TODO(jonskeet): Check that is_packable is equivalent to + // is_repeated && wt in { VARINT, FIXED32, FIXED64 }. // It looks like it is... if (field->is_packable()) { printer->Print( @@ -490,7 +495,7 @@ int MessageGenerator::GetFieldOrdinal(const FieldDescriptor* descriptor) { FieldGeneratorBase* MessageGenerator::CreateFieldGeneratorInternal( const FieldDescriptor* descriptor) { - return CreateFieldGenerator(descriptor, GetFieldOrdinal(descriptor)); + return CreateFieldGenerator(descriptor, GetFieldOrdinal(descriptor), this->options()); } } // namespace csharp diff --git a/src/google/protobuf/compiler/csharp/csharp_message.h b/src/google/protobuf/compiler/csharp/csharp_message.h index f0c49ac90..f794d68df 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message.h +++ b/src/google/protobuf/compiler/csharp/csharp_message.h @@ -47,7 +47,7 @@ class FieldGeneratorBase; class MessageGenerator : public SourceGeneratorBase { public: - MessageGenerator(const Descriptor* descriptor); + MessageGenerator(const Descriptor* descriptor, const Options* options); ~MessageGenerator(); void GenerateCloningCode(io::Printer* printer); diff --git a/src/google/protobuf/compiler/csharp/csharp_message_field.cc b/src/google/protobuf/compiler/csharp/csharp_message_field.cc index f81f769b2..338692f8c 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_message_field.cc @@ -41,6 +41,7 @@ #include #include #include +#include namespace google { namespace protobuf { @@ -48,8 +49,9 @@ namespace compiler { namespace csharp { MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor* descriptor, - int fieldOrdinal) - : FieldGeneratorBase(descriptor, fieldOrdinal) { + int fieldOrdinal, + const Options *options) + : FieldGeneratorBase(descriptor, fieldOrdinal, options) { variables_["has_property_check"] = name() + "_ != null"; variables_["has_not_property_check"] = name() + "_ == null"; } @@ -143,9 +145,11 @@ void MessageFieldGenerator::GenerateCodecCode(io::Printer* printer) { "pb::FieldCodec.ForMessage($tag$, $type_name$.Parser)"); } -MessageOneofFieldGenerator::MessageOneofFieldGenerator(const FieldDescriptor* descriptor, - int fieldOrdinal) - : MessageFieldGenerator(descriptor, fieldOrdinal) { +MessageOneofFieldGenerator::MessageOneofFieldGenerator( + const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options *options) + : MessageFieldGenerator(descriptor, fieldOrdinal, options) { SetCommonOneofFieldVariables(&variables_); } diff --git a/src/google/protobuf/compiler/csharp/csharp_message_field.h b/src/google/protobuf/compiler/csharp/csharp_message_field.h index dc6e4dc5a..7d614756f 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message_field.h +++ b/src/google/protobuf/compiler/csharp/csharp_message_field.h @@ -43,7 +43,9 @@ namespace csharp { class MessageFieldGenerator : public FieldGeneratorBase { public: - MessageFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + MessageFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options *options); ~MessageFieldGenerator(); virtual void GenerateCodecCode(io::Printer* printer); @@ -65,7 +67,9 @@ class MessageFieldGenerator : public FieldGeneratorBase { class MessageOneofFieldGenerator : public MessageFieldGenerator { public: - MessageOneofFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + MessageOneofFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options *options); ~MessageOneofFieldGenerator(); virtual void GenerateCloningCode(io::Printer* printer); diff --git a/src/google/protobuf/compiler/csharp/csharp_options.h b/src/google/protobuf/compiler/csharp/csharp_options.h new file mode 100644 index 000000000..4079bf7f3 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_options.h @@ -0,0 +1,86 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_OPTIONS_H__ +#define GOOGLE_PROTOBUF_COMPILER_CSHARP_OPTIONS_H__ + +#include + +#include +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +// Generator options (used by csharp_generator.cc): +struct Options { + Options() : + file_extension(".cs"), + base_namespace(""), + base_namespace_specified(false), + internal_access(false), + legacy_enum_values(false) { + } + // Extension of the generated file. Defaults to ".cs" + string file_extension; + // Base namespace to use to create directory hierarchy. Defaults to "". + // This option allows the simple creation of a conventional C# file layout, + // where directories are created relative to a project-specific base + // namespace. For example, in a project with a base namespace of PetShop, a + // proto of user.proto with a C# namespace of PetShop.Model.Shared would + // generate Model/Shared/User.cs underneath the specified --csharp_out + // directory. + // + // If no base namespace is specified, all files are generated in the + // --csharp_out directory, with no subdirectories created automatically. + string base_namespace; + // Whether the base namespace has been explicitly specified by the user. + // This is required as the base namespace can be explicitly set to the empty + // string, meaning "create a full directory hierarchy, starting from the first + // segment of the namespace." + bool base_namespace_specified; + // Whether the generated classes should have accessibility level of "internal". + // Defaults to false that generates "public" classes. + bool internal_access; + // By default, C# codegen now uses PascalCased enum values names, after + // removing the enum type name as a prefix (if it *is* a prefix of the value). + // Setting this option reverts to the previous behavior of just copying the + // value name specified in the .proto file, allowing gradual migration. + // This option will be removed before final release. + bool legacy_enum_values; +}; + +} // namespace csharp +} // namespace compiler +} // namespace protobuf + + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_OPTIONS_H__ diff --git a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc index 60afd892b..3b7ca75ab 100644 --- a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc @@ -40,6 +40,7 @@ #include #include +#include #include namespace google { @@ -48,8 +49,8 @@ namespace compiler { namespace csharp { PrimitiveFieldGenerator::PrimitiveFieldGenerator( - const FieldDescriptor* descriptor, int fieldOrdinal) - : FieldGeneratorBase(descriptor, fieldOrdinal) { + const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options) + : FieldGeneratorBase(descriptor, fieldOrdinal, options) { // TODO(jonskeet): Make this cleaner... is_value_type = descriptor->type() != FieldDescriptor::TYPE_STRING && descriptor->type() != FieldDescriptor::TYPE_BYTES; @@ -163,8 +164,8 @@ void PrimitiveFieldGenerator::GenerateCodecCode(io::Printer* printer) { } PrimitiveOneofFieldGenerator::PrimitiveOneofFieldGenerator( - const FieldDescriptor* descriptor, int fieldOrdinal) - : PrimitiveFieldGenerator(descriptor, fieldOrdinal) { + const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options) + : PrimitiveFieldGenerator(descriptor, fieldOrdinal, options) { SetCommonOneofFieldVariables(&variables_); } diff --git a/src/google/protobuf/compiler/csharp/csharp_primitive_field.h b/src/google/protobuf/compiler/csharp/csharp_primitive_field.h index 8b87ebc49..5f466fc47 100644 --- a/src/google/protobuf/compiler/csharp/csharp_primitive_field.h +++ b/src/google/protobuf/compiler/csharp/csharp_primitive_field.h @@ -41,9 +41,13 @@ namespace protobuf { namespace compiler { namespace csharp { +struct Options; + class PrimitiveFieldGenerator : public FieldGeneratorBase { public: - PrimitiveFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + PrimitiveFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options *options); ~PrimitiveFieldGenerator(); virtual void GenerateCodecCode(io::Printer* printer); @@ -67,7 +71,9 @@ class PrimitiveFieldGenerator : public FieldGeneratorBase { class PrimitiveOneofFieldGenerator : public PrimitiveFieldGenerator { public: - PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options *options); ~PrimitiveOneofFieldGenerator(); virtual void GenerateCloningCode(io::Printer* printer); diff --git a/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc b/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc index 22dae43ba..f7397c0f6 100644 --- a/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc +++ b/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc @@ -43,6 +43,7 @@ #include #include #include +#include #include namespace google { @@ -50,8 +51,9 @@ namespace protobuf { namespace compiler { namespace csharp { -ReflectionClassGenerator::ReflectionClassGenerator(const FileDescriptor* file) - : SourceGeneratorBase(file), +ReflectionClassGenerator::ReflectionClassGenerator(const FileDescriptor* file, + const Options* options) + : SourceGeneratorBase(file, options), file_(file) { namespace_ = GetFileNamespace(file); reflectionClassname_ = GetReflectionClassUnqualifiedName(file); @@ -72,7 +74,7 @@ void ReflectionClassGenerator::Generate(io::Printer* printer) { if (file_->enum_type_count() > 0) { printer->Print("#region Enums\n"); for (int i = 0; i < file_->enum_type_count(); i++) { - EnumGenerator enumGenerator(file_->enum_type(i)); + EnumGenerator enumGenerator(file_->enum_type(i), this->options()); enumGenerator.Generate(printer); } printer->Print("#endregion\n"); @@ -83,7 +85,7 @@ void ReflectionClassGenerator::Generate(io::Printer* printer) { if (file_->message_type_count() > 0) { printer->Print("#region Messages\n"); for (int i = 0; i < file_->message_type_count(); i++) { - MessageGenerator messageGenerator(file_->message_type(i)); + MessageGenerator messageGenerator(file_->message_type(i), this->options()); messageGenerator.Generate(printer); } printer->Print("#endregion\n"); diff --git a/src/google/protobuf/compiler/csharp/csharp_reflection_class.h b/src/google/protobuf/compiler/csharp/csharp_reflection_class.h index 0a5b8ed57..e0c69f31f 100644 --- a/src/google/protobuf/compiler/csharp/csharp_reflection_class.h +++ b/src/google/protobuf/compiler/csharp/csharp_reflection_class.h @@ -43,7 +43,7 @@ namespace csharp { class ReflectionClassGenerator : public SourceGeneratorBase { public: - ReflectionClassGenerator(const FileDescriptor* file); + ReflectionClassGenerator(const FileDescriptor* file, const Options* options); ~ReflectionClassGenerator(); void Generate(io::Printer* printer); @@ -56,7 +56,9 @@ class ReflectionClassGenerator : public SourceGeneratorBase { void WriteIntroduction(io::Printer* printer); void WriteDescriptor(io::Printer* printer); - void WriteGeneratedCodeInfo(const Descriptor* descriptor, io::Printer* printer, bool last); + void WriteGeneratedCodeInfo(const Descriptor* descriptor, + io::Printer* printer, + bool last); GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ReflectionClassGenerator); }; diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc b/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc index 3a11b75d1..1befdc151 100644 --- a/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc @@ -48,8 +48,8 @@ namespace compiler { namespace csharp { RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator( - const FieldDescriptor* descriptor, int fieldOrdinal) - : FieldGeneratorBase(descriptor, fieldOrdinal) { + const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options) + : FieldGeneratorBase(descriptor, fieldOrdinal, options) { } RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() { diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.h b/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.h index ee50eef01..819b58326 100644 --- a/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.h +++ b/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.h @@ -45,7 +45,9 @@ namespace csharp { // should probably have a RepeatedFieldGeneratorBase. class RepeatedEnumFieldGenerator : public FieldGeneratorBase { public: - RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options *options); ~RepeatedEnumFieldGenerator(); virtual void GenerateCloningCode(io::Printer* printer); diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc b/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc index fc12faed8..d51e638a9 100644 --- a/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc @@ -49,8 +49,8 @@ namespace compiler { namespace csharp { RepeatedMessageFieldGenerator::RepeatedMessageFieldGenerator( - const FieldDescriptor* descriptor, int fieldOrdinal) - : FieldGeneratorBase(descriptor, fieldOrdinal) { + const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options) + : FieldGeneratorBase(descriptor, fieldOrdinal, options) { } RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() { @@ -66,10 +66,12 @@ void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) { // "create single field generator for this repeated field" // function, but it doesn't seem worth it for just this. if (IsWrapperType(descriptor_)) { - scoped_ptr single_generator(new WrapperFieldGenerator(descriptor_, fieldOrdinal_)); + scoped_ptr single_generator( + new WrapperFieldGenerator(descriptor_, fieldOrdinal_, this->options())); single_generator->GenerateCodecCode(printer); } else { - scoped_ptr single_generator(new MessageFieldGenerator(descriptor_, fieldOrdinal_)); + scoped_ptr single_generator( + new MessageFieldGenerator(descriptor_, fieldOrdinal_, this->options())); single_generator->GenerateCodecCode(printer); } printer->Print(";\n"); diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.h b/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.h index cf601c7e0..6e33648b6 100644 --- a/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.h +++ b/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.h @@ -41,9 +41,13 @@ namespace protobuf { namespace compiler { namespace csharp { +struct Options; + class RepeatedMessageFieldGenerator : public FieldGeneratorBase { public: - RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options *options); ~RepeatedMessageFieldGenerator(); virtual void GenerateCloningCode(io::Printer* printer); diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc index 5fe0b203d..bee3f3630 100644 --- a/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc @@ -48,8 +48,8 @@ namespace compiler { namespace csharp { RepeatedPrimitiveFieldGenerator::RepeatedPrimitiveFieldGenerator( - const FieldDescriptor* descriptor, int fieldOrdinal) - : FieldGeneratorBase(descriptor, fieldOrdinal) { + const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options) + : FieldGeneratorBase(descriptor, fieldOrdinal, options) { } RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() { diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h b/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h index f1ceeb507..a59348a95 100644 --- a/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h +++ b/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h @@ -43,7 +43,7 @@ namespace csharp { class RepeatedPrimitiveFieldGenerator : public FieldGeneratorBase { public: - RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options); ~RepeatedPrimitiveFieldGenerator(); virtual void GenerateCloningCode(io::Printer* printer); diff --git a/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc b/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc index 735d164a4..16411e49c 100644 --- a/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc +++ b/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc @@ -39,14 +39,17 @@ #include #include +#include +#include namespace google { namespace protobuf { namespace compiler { namespace csharp { -SourceGeneratorBase::SourceGeneratorBase(const FileDescriptor* descriptor) - : descriptor_(descriptor) { +SourceGeneratorBase::SourceGeneratorBase(const FileDescriptor* descriptor, + const Options *options) + : descriptor_(descriptor), options_(options) { } SourceGeneratorBase::~SourceGeneratorBase() { @@ -57,7 +60,11 @@ void SourceGeneratorBase::WriteGeneratedCodeAttributes(io::Printer* printer) { } std::string SourceGeneratorBase::class_access_level() { - return IsDescriptorProto(descriptor_) ? "internal" : "public"; // public_classes is always on. + return (IsDescriptorProto(descriptor_) || this->options()->internal_access) ? "internal" : "public"; +} + +const Options* SourceGeneratorBase::options() { + return this->options_; } } // namespace csharp diff --git a/src/google/protobuf/compiler/csharp/csharp_source_generator_base.h b/src/google/protobuf/compiler/csharp/csharp_source_generator_base.h index 6caef171c..2e7345812 100644 --- a/src/google/protobuf/compiler/csharp/csharp_source_generator_base.h +++ b/src/google/protobuf/compiler/csharp/csharp_source_generator_base.h @@ -40,17 +40,21 @@ namespace protobuf { namespace compiler { namespace csharp { +struct Options; + class SourceGeneratorBase { protected: - SourceGeneratorBase(const FileDescriptor* descriptor); + SourceGeneratorBase(const FileDescriptor* descriptor, const Options* options); virtual ~SourceGeneratorBase(); std::string class_access_level(); + const Options* options(); void WriteGeneratedCodeAttributes(io::Printer* printer); private: const FileDescriptor* descriptor_; + const Options *options_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SourceGeneratorBase); }; diff --git a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc index 6a3750e00..5cb86b6b2 100644 --- a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc @@ -39,6 +39,7 @@ #include #include +#include #include namespace google { @@ -47,8 +48,8 @@ namespace compiler { namespace csharp { WrapperFieldGenerator::WrapperFieldGenerator(const FieldDescriptor* descriptor, - int fieldOrdinal) - : FieldGeneratorBase(descriptor, fieldOrdinal) { + int fieldOrdinal, const Options *options) + : FieldGeneratorBase(descriptor, fieldOrdinal, options) { variables_["has_property_check"] = name() + "_ != null"; variables_["has_not_property_check"] = name() + "_ == null"; const FieldDescriptor* wrapped_field = descriptor->message_type()->field(0); @@ -151,9 +152,9 @@ void WrapperFieldGenerator::GenerateCodecCode(io::Printer* printer) { } } -WrapperOneofFieldGenerator::WrapperOneofFieldGenerator(const FieldDescriptor* descriptor, - int fieldOrdinal) - : WrapperFieldGenerator(descriptor, fieldOrdinal) { +WrapperOneofFieldGenerator::WrapperOneofFieldGenerator( + const FieldDescriptor* descriptor, int fieldOrdinal, const Options *options) + : WrapperFieldGenerator(descriptor, fieldOrdinal, options) { SetCommonOneofFieldVariables(&variables_); } diff --git a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.h b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.h index 6e2414af8..250dfd254 100644 --- a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.h +++ b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.h @@ -41,9 +41,13 @@ namespace protobuf { namespace compiler { namespace csharp { +struct Options; + class WrapperFieldGenerator : public FieldGeneratorBase { public: - WrapperFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + WrapperFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options *options); ~WrapperFieldGenerator(); virtual void GenerateCodecCode(io::Printer* printer); @@ -65,7 +69,9 @@ class WrapperFieldGenerator : public FieldGeneratorBase { class WrapperOneofFieldGenerator : public WrapperFieldGenerator { public: - WrapperOneofFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal); + WrapperOneofFieldGenerator(const FieldDescriptor* descriptor, + int fieldOrdinal, + const Options *options); ~WrapperOneofFieldGenerator(); virtual void GenerateMembers(io::Printer* printer); diff --git a/src/google/protobuf/compiler/importer_unittest.cc b/src/google/protobuf/compiler/importer_unittest.cc index be19aa2e6..1b6e97007 100644 --- a/src/google/protobuf/compiler/importer_unittest.cc +++ b/src/google/protobuf/compiler/importer_unittest.cc @@ -32,25 +32,26 @@ // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. +#include + #include #include #ifndef _SHARED_PTR_H #include #endif -#include -#include -#include -#include - -#include #include #include #include +#include +#include +#include +#include #include #include #include #include +#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/compiler/java/java_context.cc b/src/google/protobuf/compiler/java/java_context.cc index 7d21fe617..0a112888c 100644 --- a/src/google/protobuf/compiler/java/java_context.cc +++ b/src/google/protobuf/compiler/java/java_context.cc @@ -43,7 +43,7 @@ namespace compiler { namespace java { Context::Context(const FileDescriptor* file) - : name_resolver_(new ClassNameResolver) { + : name_resolver_(new ClassNameResolver), enforce_lite_(false) { InitializeFieldGeneratorInfo(file); } @@ -189,6 +189,13 @@ const OneofGeneratorInfo* Context::GetOneofGeneratorInfo( return result; } +// Does this message class have generated parsing, serialization, and other +// standard methods for which reflection-based fallback implementations exist? +bool Context::HasGeneratedMethods(const Descriptor* descriptor) const { + return enforce_lite_ || descriptor->file()->options().optimize_for() != + FileOptions::CODE_SIZE; +} + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_context.h b/src/google/protobuf/compiler/java/java_context.h index 5b595d07f..a480e45dc 100644 --- a/src/google/protobuf/compiler/java/java_context.h +++ b/src/google/protobuf/compiler/java/java_context.h @@ -46,6 +46,7 @@ namespace protobuf { class FieldDescriptor; class OneofDescriptor; class Descriptor; + class EnumDescriptor; namespace compiler { namespace java { class ClassNameResolver; // name_resolver.h @@ -78,6 +79,20 @@ class Context { const OneofGeneratorInfo* GetOneofGeneratorInfo( const OneofDescriptor* oneof) const; + // Enforces all the files (including transitive dependencies) to use + // LiteRuntime. + void SetEnforceLite(bool enforce_lite) { + enforce_lite_ = enforce_lite; + } + + bool EnforceLite() const { + return enforce_lite_; + } + + // Does this message class have generated parsing, serialization, and other + // standard methods for which reflection-based fallback implementations exist? + bool HasGeneratedMethods(const Descriptor* descriptor) const; + private: void InitializeFieldGeneratorInfo(const FileDescriptor* file); void InitializeFieldGeneratorInfoForMessage(const Descriptor* message); @@ -87,6 +102,7 @@ class Context { google::protobuf::scoped_ptr name_resolver_; map field_generator_info_map_; map oneof_generator_info_map_; + bool enforce_lite_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Context); }; diff --git a/src/google/protobuf/compiler/java/java_doc_comment.cc b/src/google/protobuf/compiler/java/java_doc_comment.cc index 663f0c97f..0b5caba43 100644 --- a/src/google/protobuf/compiler/java/java_doc_comment.cc +++ b/src/google/protobuf/compiler/java/java_doc_comment.cc @@ -120,9 +120,7 @@ static void WriteDocCommentBodyForLocation( lines.pop_back(); } - printer->Print( - " *\n" - " *

\n");
+    printer->Print(" * 
\n");
     for (int i = 0; i < lines.size(); i++) {
       // Most lines should start with a space.  Watch out for lines that start
       // with a /, since putting that right after the leading asterisk will
@@ -133,7 +131,9 @@ static void WriteDocCommentBodyForLocation(
         printer->Print(" *$line$\n", "line", lines[i]);
       }
     }
-    printer->Print(" * 
\n"); + printer->Print( + " *
\n" + " *\n"); } } @@ -163,12 +163,12 @@ static string FirstLineOf(const string& value) { } void WriteMessageDocComment(io::Printer* printer, const Descriptor* message) { - printer->Print( - "/**\n" - " * Protobuf type {@code $fullname$}\n", - "fullname", EscapeJavadoc(message->full_name())); + printer->Print("/**\n"); WriteDocCommentBody(printer, message); - printer->Print(" */\n"); + printer->Print( + " * Protobuf type {@code $fullname$}\n" + " */\n", + "fullname", EscapeJavadoc(message->full_name())); } void WriteFieldDocComment(io::Printer* printer, const FieldDescriptor* field) { @@ -176,55 +176,55 @@ void WriteFieldDocComment(io::Printer* printer, const FieldDescriptor* field) { // etc., but in practice everyone already knows the difference between these // so it's redundant information. - // We use the field declaration as the first line of the comment, e.g.: + // We start the comment with the main body based on the comments from the + // .proto file (if present). We then end with the field declaration, e.g.: // optional string foo = 5; - // This communicates a lot of information about the field in a small space. // If the field is a group, the debug string might end with {. + printer->Print("/**\n"); + WriteDocCommentBody(printer, field); printer->Print( - "/**\n" " * $def$\n", "def", EscapeJavadoc(FirstLineOf(field->DebugString()))); - WriteDocCommentBody(printer, field); printer->Print(" */\n"); } void WriteEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_) { - printer->Print( - "/**\n" - " * Protobuf enum {@code $fullname$}\n", - "fullname", EscapeJavadoc(enum_->full_name())); + printer->Print("/**\n"); WriteDocCommentBody(printer, enum_); - printer->Print(" */\n"); + printer->Print( + " * Protobuf enum {@code $fullname$}\n" + " */\n", + "fullname", EscapeJavadoc(enum_->full_name())); } void WriteEnumValueDocComment(io::Printer* printer, const EnumValueDescriptor* value) { - printer->Print( - "/**\n" - " * $def$\n", - "def", EscapeJavadoc(FirstLineOf(value->DebugString()))); + printer->Print("/**\n"); WriteDocCommentBody(printer, value); - printer->Print(" */\n"); + printer->Print( + " * $def$\n" + " */\n", + "def", EscapeJavadoc(FirstLineOf(value->DebugString()))); } void WriteServiceDocComment(io::Printer* printer, const ServiceDescriptor* service) { - printer->Print( - "/**\n" - " * Protobuf service {@code $fullname$}\n", - "fullname", EscapeJavadoc(service->full_name())); + printer->Print("/**\n"); WriteDocCommentBody(printer, service); - printer->Print(" */\n"); + printer->Print( + " * Protobuf service {@code $fullname$}\n" + " */\n", + "fullname", EscapeJavadoc(service->full_name())); } void WriteMethodDocComment(io::Printer* printer, const MethodDescriptor* method) { - printer->Print( - "/**\n" - " * $def$\n", - "def", EscapeJavadoc(FirstLineOf(method->DebugString()))); + printer->Print("/**\n"); WriteDocCommentBody(printer, method); - printer->Print(" */\n"); + printer->Print( + " * $def$\n" + " */\n", + "def", EscapeJavadoc(FirstLineOf(method->DebugString()))); } } // namespace java diff --git a/src/google/protobuf/compiler/java/java_enum.cc b/src/google/protobuf/compiler/java/java_enum.cc index 5fc9b002f..947b80e41 100644 --- a/src/google/protobuf/compiler/java/java_enum.cc +++ b/src/google/protobuf/compiler/java/java_enum.cc @@ -64,6 +64,7 @@ EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, bool immutable_api, Context* context) : descriptor_(descriptor), immutable_api_(immutable_api), + context_(context), name_resolver_(context->GetNameResolver()) { for (int i = 0; i < descriptor_->value_count(); i++) { const EnumValueDescriptor* value = descriptor_->value(i); @@ -91,6 +92,16 @@ void EnumGenerator::Generate(io::Printer* printer) { "classname", descriptor_->name()); printer->Indent(); + bool ordinal_is_index = true; + string index_text = "ordinal()"; + for (int i = 0; i < canonical_values_.size(); i++) { + if (canonical_values_[i]->index() != i) { + ordinal_is_index = false; + index_text = "index"; + break; + } + } + for (int i = 0; i < canonical_values_.size(); i++) { map vars; vars["name"] = canonical_values_[i]->name(); @@ -100,12 +111,21 @@ void EnumGenerator::Generate(io::Printer* printer) { if (canonical_values_[i]->options().deprecated()) { printer->Print("@java.lang.Deprecated\n"); } - printer->Print(vars, - "$name$($index$, $number$),\n"); + if (ordinal_is_index) { + printer->Print(vars, + "$name$($number$),\n"); + } else { + printer->Print(vars, + "$name$($index$, $number$),\n"); + } } if (SupportUnknownEnumValue(descriptor_->file())) { - printer->Print("UNRECOGNIZED(-1, -1),\n"); + if (ordinal_is_index) { + printer->Print("UNRECOGNIZED(-1),\n"); + } else { + printer->Print("UNRECOGNIZED(-1, -1),\n"); + } } printer->Print( @@ -140,17 +160,33 @@ void EnumGenerator::Generate(io::Printer* printer) { "\n" "public final int getNumber() {\n"); if (SupportUnknownEnumValue(descriptor_->file())) { - printer->Print( - " if (index == -1) {\n" - " throw new java.lang.IllegalArgumentException(\n" - " \"Can't get the number of an unknown enum value.\");\n" - " }\n"); + if (ordinal_is_index) { + printer->Print( + " if (this == UNRECOGNIZED) {\n" + " throw new java.lang.IllegalArgumentException(\n" + " \"Can't get the number of an unknown enum value.\");\n" + " }\n"); + } else { + printer->Print( + " if (index == -1) {\n" + " throw new java.lang.IllegalArgumentException(\n" + " \"Can't get the number of an unknown enum value.\");\n" + " }\n"); + } } printer->Print( " return value;\n" "}\n" "\n" + "/**\n" + " * @deprecated Use {@link #forNumber(int)} instead.\n" + " */\n" + "@java.lang.Deprecated\n" "public static $classname$ valueOf(int value) {\n" + " return forNumber(value);\n" + "}\n" + "\n" + "public static $classname$ forNumber(int value) {\n" " switch (value) {\n", "classname", descriptor_->name()); printer->Indent(); @@ -178,7 +214,7 @@ void EnumGenerator::Generate(io::Printer* printer) { " $classname$> internalValueMap =\n" " new com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n" " public $classname$ findValueByNumber(int number) {\n" - " return $classname$.valueOf(number);\n" + " return $classname$.forNumber(number);\n" " }\n" " };\n" "\n", @@ -187,18 +223,19 @@ void EnumGenerator::Generate(io::Printer* printer) { // ----------------------------------------------------------------- // Reflection - if (HasDescriptorMethods(descriptor_)) { + if (HasDescriptorMethods(descriptor_, context_->EnforceLite())) { printer->Print( "public final com.google.protobuf.Descriptors.EnumValueDescriptor\n" " getValueDescriptor() {\n" - " return getDescriptor().getValues().get(index);\n" + " return getDescriptor().getValues().get($index_text$);\n" "}\n" "public final com.google.protobuf.Descriptors.EnumDescriptor\n" " getDescriptorForType() {\n" " return getDescriptor();\n" "}\n" "public static final com.google.protobuf.Descriptors.EnumDescriptor\n" - " getDescriptor() {\n"); + " getDescriptor() {\n", + "index_text", index_text); // TODO(kenton): Cache statically? Note that we can't access descriptors // at module init time because it wouldn't work with descriptor.proto, but @@ -229,7 +266,7 @@ void EnumGenerator::Generate(io::Printer* printer) { " (com.google.protobuf.Descriptors.FileDescriptor)\n" " m.invoke(immutableFileClass);\n" " return file.getEnumTypes().get($index$);\n" - "} catch (Exception e) {\n" + "} catch (java.lang.Exception e) {\n" // Immutable classes cannot be found. Proceed as if custom options // don't exist. "}\n", @@ -304,16 +341,27 @@ void EnumGenerator::Generate(io::Printer* printer) { "}\n" "\n"); - printer->Print("private final int index;\n"); + if (!ordinal_is_index) { + printer->Print("private final int index;\n"); + } } // ----------------------------------------------------------------- printer->Print( - "private final int value;\n\n" - "private $classname$(int index, int value) {\n", - "classname", descriptor_->name()); - if (HasDescriptorMethods(descriptor_)) { + "private final int value;\n\n"); + + if (ordinal_is_index) { + printer->Print( + "private $classname$(int value) {\n", + "classname", descriptor_->name()); + } else { + printer->Print( + "private $classname$(int index, int value) {\n", + "classname", descriptor_->name()); + } + if (HasDescriptorMethods(descriptor_, context_->EnforceLite()) && + !ordinal_is_index) { printer->Print(" this.index = index;\n"); } printer->Print( diff --git a/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc index 558da968e..3e54be3d5 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.cc +++ b/src/google/protobuf/compiler/java/java_enum_field.cc @@ -68,21 +68,14 @@ void SetEnumVariables(const FieldDescriptor* descriptor, (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); (*variables)["default_number"] = SimpleItoa( descriptor->default_value_enum()->number()); - if (descriptor->is_packed()) { - (*variables)["tag"] = SimpleItoa(internal::WireFormatLite::MakeTag( - descriptor->number(), - internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED)); - } else { - (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); - } + (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); (*variables)["tag_size"] = SimpleItoa( internal::WireFormat::TagSize(descriptor->number(), GetType(descriptor))); // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + (*variables)["on_changed"] = "onChanged();"; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -198,7 +191,7 @@ GenerateMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" - " $type$ result = $type$.valueOf($name$_);\n" + " $type$ result = $type$.forNumber($name$_);\n" " return result == null ? $unknown$ : result;\n" "}\n"); } @@ -231,7 +224,7 @@ GenerateBuilderMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" - " $type$ result = $type$.valueOf($name$_);\n" + " $type$ result = $type$.forNumber($name$_);\n" " return result == null ? $unknown$ : result;\n" "}\n"); WriteFieldDocComment(printer, descriptor_); @@ -311,7 +304,7 @@ GenerateParsingCode(io::Printer* printer) const { } else { printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n" + "$type$ value = $type$.forNumber(rawValue);\n" "if (value == null) {\n"); if (PreserveUnknownFields(descriptor_->containing_type())) { printer->Print(variables_, @@ -405,7 +398,7 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" " if ($has_oneof_case_message$) {\n" - " $type$ result = $type$.valueOf((java.lang.Integer) $oneof_name$_);\n" + " $type$ result = $type$.forNumber((java.lang.Integer) $oneof_name$_);\n" " return result == null ? $unknown$ : result;\n" " }\n" " return $default$;\n" @@ -443,7 +436,7 @@ GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" " if ($has_oneof_case_message$) {\n" - " $type$ result = $type$.valueOf((java.lang.Integer) $oneof_name$_);\n" + " $type$ result = $type$.forNumber((java.lang.Integer) $oneof_name$_);\n" " return result == null ? $unknown$ : result;\n" " }\n" " return $default$;\n" @@ -500,7 +493,7 @@ GenerateParsingCode(io::Printer* printer) const { } else { printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n" + "$type$ value = $type$.forNumber(rawValue);\n" "if (value == null) {\n"); if (PreserveUnknownFields(descriptor_->containing_type())) { printer->Print(variables_, @@ -613,7 +606,7 @@ GenerateMembers(io::Printer* printer) const { " new com.google.protobuf.Internal.ListAdapter.Converter<\n" " java.lang.Integer, $type$>() {\n" " public $type$ convert(java.lang.Integer from) {\n" - " $type$ result = $type$.valueOf(from);\n" + " $type$ result = $type$.forNumber(from);\n" " return result == null ? $unknown$ : result;\n" " }\n" " };\n"); @@ -649,7 +642,7 @@ GenerateMembers(io::Printer* printer) const { } if (descriptor_->is_packed() && - HasGeneratedMethods(descriptor_->containing_type())) { + context_->HasGeneratedMethods(descriptor_->containing_type())) { printer->Print(variables_, "private int $name$MemoizedSerializedSize;\n"); } @@ -846,7 +839,7 @@ GenerateParsingCode(io::Printer* printer) const { } else { printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n" + "$type$ value = $type$.forNumber(rawValue);\n" "if (value == null) {\n"); if (PreserveUnknownFields(descriptor_->containing_type())) { printer->Print(variables_, diff --git a/src/google/protobuf/compiler/java/java_enum_field_lite.cc b/src/google/protobuf/compiler/java/java_enum_field_lite.cc index e3e87c58f..908d6db41 100644 --- a/src/google/protobuf/compiler/java/java_enum_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.cc @@ -75,8 +75,6 @@ void SetEnumVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -179,7 +177,7 @@ GenerateMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" - " $type$ result = $type$.valueOf($name$_);\n" + " $type$ result = $type$.forNumber($name$_);\n" " return result == null ? $unknown$ : result;\n" "}\n"); @@ -228,7 +226,7 @@ GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public Builder set$capitalized_name$Value(int value) {\n" " copyOnWrite();\n" - " instance.set$capitalized_name$Value(int value);\n" + " instance.set$capitalized_name$Value(value);\n" " return this;\n" "}\n"); } @@ -264,17 +262,15 @@ GenerateInitializationCode(io::Printer* printer) const { } void ImmutableEnumFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { +GenerateVisitCode(io::Printer* printer) const { if (SupportFieldPresence(descriptor_->file())) { printer->Print(variables_, - "if (other.has$capitalized_name$()) {\n" - " set$capitalized_name$(other.get$capitalized_name$());\n" - "}\n"); + "$name$_ = visitor.visitInt(has$capitalized_name$(), $name$_,\n" + " other.has$capitalized_name$(), other.$name$_);\n"); } else if (SupportUnknownEnumValue(descriptor_->file())) { printer->Print(variables_, - "if (other.$name$_ != $default_number$) {\n" - " set$capitalized_name$Value(other.get$capitalized_name$Value());\n" - "}\n"); + "$name$_ = visitor.visitInt($name$_ != $default_number$, $name$_," + " other.$name$_ != $default_number$, other.$name$_);\n"); } else { GOOGLE_LOG(FATAL) << "Can't reach here."; } @@ -295,7 +291,7 @@ GenerateParsingCode(io::Printer* printer) const { } else { printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n" + "$type$ value = $type$.forNumber(rawValue);\n" "if (value == null) {\n"); if (PreserveUnknownFields(descriptor_->containing_type())) { printer->Print(variables_, @@ -389,7 +385,7 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" " if ($has_oneof_case_message$) {\n" - " $type$ result = $type$.valueOf((java.lang.Integer) $oneof_name$_);\n" + " $type$ result = $type$.forNumber((java.lang.Integer) $oneof_name$_);\n" " return result == null ? $unknown$ : result;\n" " }\n" " return $default$;\n" @@ -468,14 +464,10 @@ GenerateBuilderMembers(io::Printer* printer) const { } void ImmutableEnumOneofFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { - if (SupportUnknownEnumValue(descriptor_->file())) { - printer->Print(variables_, - "set$capitalized_name$Value(other.get$capitalized_name$Value());\n"); - } else { - printer->Print(variables_, - "set$capitalized_name$(other.get$capitalized_name$());\n"); - } +GenerateVisitCode(io::Printer* printer) const { + printer->Print(variables_, + "$oneof_name$_ = visitor.visitOneofInt(\n" + " $has_oneof_case_message$, $oneof_name$_, other.$oneof_name$_);\n"); } void ImmutableEnumOneofFieldLiteGenerator:: @@ -488,7 +480,7 @@ GenerateParsingCode(io::Printer* printer) const { } else { printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n" + "$type$ value = $type$.forNumber(rawValue);\n" "if (value == null) {\n"); if (PreserveUnknownFields(descriptor_->containing_type())) { printer->Print(variables_, @@ -602,7 +594,7 @@ GenerateMembers(io::Printer* printer) const { " new com.google.protobuf.Internal.ListAdapter.Converter<\n" " java.lang.Integer, $type$>() {\n" " public $type$ convert(java.lang.Integer from) {\n" - " $type$ result = $type$.valueOf(from);\n" + " $type$ result = $type$.forNumber(from);\n" " return result == null ? $unknown$ : result;\n" " }\n" " };\n"); @@ -638,7 +630,7 @@ GenerateMembers(io::Printer* printer) const { } if (descriptor_->options().packed() && - HasGeneratedMethods(descriptor_->containing_type())) { + context_->HasGeneratedMethods(descriptor_->containing_type())) { printer->Print(variables_, "private int $name$MemoizedSerializedSize;\n"); } @@ -647,7 +639,8 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private void ensure$capitalized_name$IsMutable() {\n" " if (!$is_mutable$) {\n" - " $name$_ = newIntList($name$_);\n" + " $name$_ =\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" " }\n" "}\n"); WriteFieldDocComment(printer, descriptor_); @@ -807,22 +800,9 @@ GenerateInitializationCode(io::Printer* printer) const { } void RepeatedImmutableEnumFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { - // The code below does two optimizations: - // 1. If the other list is empty, there's nothing to do. This ensures we - // don't allocate a new array if we already have an immutable one. - // 2. If the other list is non-empty and our current list is empty, we can - // reuse the other list which is guaranteed to be immutable. +GenerateVisitCode(io::Printer* printer) const { printer->Print(variables_, - "if (!other.$name$_.isEmpty()) {\n" - " if ($name$_.isEmpty()) {\n" - " $name$_ = other.$name$_;\n" - " } else {\n" - " ensure$capitalized_name$IsMutable();\n" - " $name$_.addAll(other.$name$_);\n" - " }\n" - " $on_changed$\n" - "}\n"); + "$name$_= visitor.visitIntList($name$_, other.$name$_);\n"); } void RepeatedImmutableEnumFieldLiteGenerator:: @@ -834,27 +814,23 @@ GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { void RepeatedImmutableEnumFieldLiteGenerator:: GenerateParsingCode(io::Printer* printer) const { // Read and store the enum + printer->Print(variables_, + "if (!$is_mutable$) {\n" + " $name$_ =\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" + "}\n"); + if (SupportUnknownEnumValue(descriptor_->file())) { printer->Print(variables_, - "int rawValue = input.readEnum();\n" - "if (!$is_mutable$) {\n" - " $name$_ = newIntList();\n" - "}\n" - "$name$_.addInt(rawValue);\n"); + "$name$_.addInt(input.readEnum());\n"); } else { printer->Print(variables_, "int rawValue = input.readEnum();\n" - "$type$ value = $type$.valueOf(rawValue);\n" - "if (value == null) {\n"); - if (PreserveUnknownFields(descriptor_->containing_type())) { - printer->Print(variables_, - " super.mergeVarintField($number$, rawValue);\n"); - } - printer->Print(variables_, + "$type$ value = $type$.forNumber(rawValue);\n" + "if (value == null) {\n" + // We store the unknown value in unknown fields. + " super.mergeVarintField($number$, rawValue);\n" "} else {\n" - " if (!$is_mutable$) {\n" - " $name$_ = newIntList();\n" - " }\n" " $name$_.addInt(rawValue);\n" "}\n"); } @@ -862,7 +838,11 @@ GenerateParsingCode(io::Printer* printer) const { void RepeatedImmutableEnumFieldLiteGenerator:: GenerateParsingCodeFromPacked(io::Printer* printer) const { - // Wrap GenerateParsingCode's contents with a while loop. + printer->Print(variables_, + "if (!$is_mutable$) {\n" + " $name$_ =\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" + "}\n"); printer->Print(variables_, "int length = input.readRawVarint32();\n" @@ -870,7 +850,21 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const { "while(input.getBytesUntilLimit() > 0) {\n"); printer->Indent(); - GenerateParsingCode(printer); + // Read and store the enum + if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "$name$_.addInt(input.readEnum());\n"); + } else { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$type$ value = $type$.forNumber(rawValue);\n" + "if (value == null) {\n" + // We store the unknown value in unknown fields. + " super.mergeVarintField($number$, rawValue);\n" + "} else {\n" + " $name$_.addInt(rawValue);\n" + "}\n"); + } printer->Outdent(); printer->Print(variables_, diff --git a/src/google/protobuf/compiler/java/java_enum_field_lite.h b/src/google/protobuf/compiler/java/java_enum_field_lite.h index 2c41c3e4c..9201b8d66 100644 --- a/src/google/protobuf/compiler/java/java_enum_field_lite.h +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.h @@ -67,7 +67,7 @@ class ImmutableEnumFieldLiteGenerator : public ImmutableFieldLiteGenerator { void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateParsingDoneCode(io::Printer* printer) const; @@ -101,7 +101,7 @@ class ImmutableEnumOneofFieldLiteGenerator void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; @@ -127,7 +127,7 @@ class RepeatedImmutableEnumFieldLiteGenerator void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateParsingCodeFromPacked(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/java/java_enum_lite.cc b/src/google/protobuf/compiler/java/java_enum_lite.cc index ed415eeed..c22da8d77 100644 --- a/src/google/protobuf/compiler/java/java_enum_lite.cc +++ b/src/google/protobuf/compiler/java/java_enum_lite.cc @@ -141,7 +141,15 @@ void EnumLiteGenerator::Generate(io::Printer* printer) { " return value;\n" "}\n" "\n" + "/**\n" + " * @deprecated Use {@link #forNumber(int)} instead.\n" + " */\n" + "@java.lang.Deprecated\n" "public static $classname$ valueOf(int value) {\n" + " return forNumber(value);\n" + "}\n" + "\n" + "public static $classname$ forNumber(int value) {\n" " switch (value) {\n", "classname", descriptor_->name()); printer->Indent(); @@ -169,7 +177,7 @@ void EnumLiteGenerator::Generate(io::Printer* printer) { " $classname$> internalValueMap =\n" " new com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n" " public $classname$ findValueByNumber(int number) {\n" - " return $classname$.valueOf(number);\n" + " return $classname$.forNumber(number);\n" " }\n" " };\n" "\n", diff --git a/src/google/protobuf/compiler/java/java_extension.cc b/src/google/protobuf/compiler/java/java_extension.cc index 4db7085e0..46b5faaa5 100644 --- a/src/google/protobuf/compiler/java/java_extension.cc +++ b/src/google/protobuf/compiler/java/java_extension.cc @@ -118,75 +118,38 @@ void ImmutableExtensionGenerator::Generate(io::Printer* printer) { "public static final int $constant_name$ = $number$;\n"); WriteFieldDocComment(printer, descriptor_); - if (HasDescriptorMethods(descriptor_->file())) { - // Non-lite extensions - if (descriptor_->extension_scope() == NULL) { - // Non-nested - printer->Print( - vars, - "public static final\n" - " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" - " $containing_type$,\n" - " $type$> $name$ = com.google.protobuf.GeneratedMessage\n" - " .newFileScopedGeneratedExtension(\n" - " $singular_type$.class,\n" - " $prototype$);\n"); - } else { - // Nested - printer->Print( - vars, - "public static final\n" - " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" - " $containing_type$,\n" - " $type$> $name$ = com.google.protobuf.GeneratedMessage\n" - " .newMessageScopedGeneratedExtension(\n" - " $scope$.getDefaultInstance(),\n" - " $index$,\n" - " $singular_type$.class,\n" - " $prototype$);\n"); - } + if (descriptor_->extension_scope() == NULL) { + // Non-nested + printer->Print( + vars, + "public static final\n" + " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ = com.google.protobuf.GeneratedMessage\n" + " .newFileScopedGeneratedExtension(\n" + " $singular_type$.class,\n" + " $prototype$);\n"); } else { - // Lite extensions - if (descriptor_->is_repeated()) { - printer->Print( - vars, - "public static final\n" - " com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n" - " $containing_type$,\n" - " $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n" - " .newRepeatedGeneratedExtension(\n" - " $containing_type$.getDefaultInstance(),\n" - " $prototype$,\n" - " $enum_map$,\n" - " $number$,\n" - " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n" - " $packed$,\n" - " $singular_type$.class);\n"); - } else { - printer->Print( - vars, - "public static final\n" - " com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n" - " $containing_type$,\n" - " $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n" - " .newSingularGeneratedExtension(\n" - " $containing_type$.getDefaultInstance(),\n" - " $default$,\n" - " $prototype$,\n" - " $enum_map$,\n" - " $number$,\n" - " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n" - " $singular_type$.class);\n"); - } + // Nested + printer->Print( + vars, + "public static final\n" + " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ = com.google.protobuf.GeneratedMessage\n" + " .newMessageScopedGeneratedExtension(\n" + " $scope$.getDefaultInstance(),\n" + " $index$,\n" + " $singular_type$.class,\n" + " $prototype$);\n"); } } int ImmutableExtensionGenerator::GenerateNonNestedInitializationCode( io::Printer* printer) { int bytecode_estimate = 0; - if (descriptor_->extension_scope() == NULL && - HasDescriptorMethods(descriptor_->file())) { - // Only applies to non-nested, non-lite extensions. + if (descriptor_->extension_scope() == NULL) { + // Only applies to non-nested extensions. printer->Print( "$name$.internalInit(descriptor.getExtensions().get($index$));\n", "name", UnderscoresToCamelCase(descriptor_), diff --git a/src/google/protobuf/compiler/java/java_extension_lite.cc b/src/google/protobuf/compiler/java/java_extension_lite.cc index e69de29bb..23261bac2 100644 --- a/src/google/protobuf/compiler/java/java_extension_lite.cc +++ b/src/google/protobuf/compiler/java/java_extension_lite.cc @@ -0,0 +1,118 @@ +// 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 + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +ImmutableExtensionLiteGenerator::ImmutableExtensionLiteGenerator( + const FieldDescriptor* descriptor, Context* context) + : descriptor_(descriptor), context_(context), + name_resolver_(context->GetNameResolver()) { + if (descriptor_->extension_scope() != NULL) { + scope_ = name_resolver_->GetImmutableClassName( + descriptor_->extension_scope()); + } else { + scope_ = name_resolver_->GetImmutableClassName(descriptor_->file()); + } +} + +ImmutableExtensionLiteGenerator::~ImmutableExtensionLiteGenerator() {} + +void ImmutableExtensionLiteGenerator::Generate(io::Printer* printer) { + map vars; + const bool kUseImmutableNames = true; + InitTemplateVars(descriptor_, scope_, kUseImmutableNames, name_resolver_, + &vars); + printer->Print(vars, + "public static final int $constant_name$ = $number$;\n"); + + WriteFieldDocComment(printer, descriptor_); + if (descriptor_->is_repeated()) { + printer->Print( + vars, + "public static final\n" + " com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n" + " .newRepeatedGeneratedExtension(\n" + " $containing_type$.getDefaultInstance(),\n" + " $prototype$,\n" + " $enum_map$,\n" + " $number$,\n" + " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n" + " $packed$,\n" + " $singular_type$.class);\n"); + } else { + printer->Print( + vars, + "public static final\n" + " com.google.protobuf.GeneratedMessageLite.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ = com.google.protobuf.GeneratedMessageLite\n" + " .newSingularGeneratedExtension(\n" + " $containing_type$.getDefaultInstance(),\n" + " $default$,\n" + " $prototype$,\n" + " $enum_map$,\n" + " $number$,\n" + " com.google.protobuf.WireFormat.FieldType.$type_constant$,\n" + " $singular_type$.class);\n"); + } +} + +int ImmutableExtensionLiteGenerator::GenerateNonNestedInitializationCode( + io::Printer* printer) { + return 0; +} + +int ImmutableExtensionLiteGenerator::GenerateRegistrationCode( + io::Printer* printer) { + printer->Print( + "registry.add($scope$.$name$);\n", + "scope", scope_, + "name", UnderscoresToCamelCase(descriptor_)); + return 7; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_extension_lite.h b/src/google/protobuf/compiler/java/java_extension_lite.h index e69de29bb..4cd49bda2 100644 --- a/src/google/protobuf/compiler/java/java_extension_lite.h +++ b/src/google/protobuf/compiler/java/java_extension_lite.h @@ -0,0 +1,76 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_LITE_H__ + +#include +#include + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +// Generates code for a lite extension, which may be within the scope of some +// message or may be at file scope. This is much simpler than FieldGenerator +// since extensions are just simple identifiers with interesting types. +class ImmutableExtensionLiteGenerator : public ExtensionGenerator { + public: + explicit ImmutableExtensionLiteGenerator(const FieldDescriptor* descriptor, + Context* context); + virtual ~ImmutableExtensionLiteGenerator(); + + virtual void Generate(io::Printer* printer); + + // Returns an estimate of the number of bytes the printed code will compile to + virtual int GenerateNonNestedInitializationCode(io::Printer* printer); + + // Returns an estimate of the number of bytes the printed code will compile to + virtual int GenerateRegistrationCode(io::Printer* printer); + + private: + const FieldDescriptor* descriptor_; + Context* context_; + ClassNameResolver* name_resolver_; + string scope_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableExtensionLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_field.cc b/src/google/protobuf/compiler/java/java_field.cc index c54347677..3c7bc5c46 100644 --- a/src/google/protobuf/compiler/java/java_field.cc +++ b/src/google/protobuf/compiler/java/java_field.cc @@ -77,7 +77,7 @@ ImmutableFieldGenerator* MakeImmutableGenerator( return new ImmutableMapFieldGenerator( field, messageBitIndex, builderBitIndex, context); } else { - if (IsLazy(field)) { + if (IsLazy(field, context->EnforceLite())) { return new RepeatedImmutableLazyMessageFieldGenerator( field, messageBitIndex, builderBitIndex, context); } else { @@ -99,7 +99,7 @@ ImmutableFieldGenerator* MakeImmutableGenerator( if (field->containing_oneof()) { switch (GetJavaType(field)) { case JAVATYPE_MESSAGE: - if (IsLazy(field)) { + if (IsLazy(field, context->EnforceLite())) { return new ImmutableLazyMessageOneofFieldGenerator( field, messageBitIndex, builderBitIndex, context); } else { @@ -119,7 +119,7 @@ ImmutableFieldGenerator* MakeImmutableGenerator( } else { switch (GetJavaType(field)) { case JAVATYPE_MESSAGE: - if (IsLazy(field)) { + if (IsLazy(field, context->EnforceLite())) { return new ImmutableLazyMessageFieldGenerator( field, messageBitIndex, builderBitIndex, context); } else { @@ -150,7 +150,7 @@ ImmutableFieldLiteGenerator* MakeImmutableLiteGenerator( return new ImmutableMapFieldLiteGenerator( field, messageBitIndex, builderBitIndex, context); } else { - if (IsLazy(field)) { + if (IsLazy(field, context->EnforceLite())) { return new RepeatedImmutableLazyMessageFieldLiteGenerator( field, messageBitIndex, builderBitIndex, context); } else { @@ -172,7 +172,7 @@ ImmutableFieldLiteGenerator* MakeImmutableLiteGenerator( if (field->containing_oneof()) { switch (GetJavaType(field)) { case JAVATYPE_MESSAGE: - if (IsLazy(field)) { + if (IsLazy(field, context->EnforceLite())) { return new ImmutableLazyMessageOneofFieldLiteGenerator( field, messageBitIndex, builderBitIndex, context); } else { @@ -192,7 +192,7 @@ ImmutableFieldLiteGenerator* MakeImmutableLiteGenerator( } else { switch (GetJavaType(field)) { case JAVATYPE_MESSAGE: - if (IsLazy(field)) { + if (IsLazy(field, context->EnforceLite())) { return new ImmutableLazyMessageFieldLiteGenerator( field, messageBitIndex, builderBitIndex, context); } else { diff --git a/src/google/protobuf/compiler/java/java_field.h b/src/google/protobuf/compiler/java/java_field.h index 0e24da24c..4dd4f57f6 100644 --- a/src/google/protobuf/compiler/java/java_field.h +++ b/src/google/protobuf/compiler/java/java_field.h @@ -105,7 +105,7 @@ class ImmutableFieldLiteGenerator { virtual void GenerateMembers(io::Printer* printer) const = 0; virtual void GenerateBuilderMembers(io::Printer* printer) const = 0; virtual void GenerateInitializationCode(io::Printer* printer) const = 0; - virtual void GenerateMergingCode(io::Printer* printer) const = 0; + virtual void GenerateVisitCode(io::Printer* printer) const = 0; virtual void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const = 0; virtual void GenerateParsingCode(io::Printer* printer) const = 0; diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc index c81723304..c53aae6b6 100644 --- a/src/google/protobuf/compiler/java/java_file.cc +++ b/src/google/protobuf/compiler/java/java_file.cc @@ -177,7 +177,7 @@ void MaybeRestartJavaMethod(io::Printer* printer, // since otherwise we hit a hardcoded limit in the jvm and javac will // then fail with the error "code too large". This limit lets our // estimates be off by a factor of two and still we're okay. - static const int bytesPerMethod = 1<<15; // aka 32K + static const int bytesPerMethod = kMaxStaticSize; if ((*bytecode_estimate) > bytesPerMethod) { ++(*method_num); @@ -193,7 +193,8 @@ void MaybeRestartJavaMethod(io::Printer* printer, } // namespace -FileGenerator::FileGenerator(const FileDescriptor* file, bool immutable_api) +FileGenerator::FileGenerator(const FileDescriptor* file, bool immutable_api, + bool enforce_lite) : file_(file), java_package_(FileJavaPackage(file, immutable_api)), message_generators_( @@ -204,6 +205,7 @@ FileGenerator::FileGenerator(const FileDescriptor* file, bool immutable_api) name_resolver_(context_->GetNameResolver()), immutable_api_(immutable_api) { classname_ = name_resolver_->GetFileClassName(file, immutable_api); + context_->SetEnforceLite(enforce_lite); generator_factory_.reset( new ImmutableGeneratorFactory(context_.get())); for (int i = 0; i < file_->message_type_count(); ++i) { @@ -262,7 +264,8 @@ void FileGenerator::Generate(io::Printer* printer) { printer->Print( "public static void registerAllExtensions(\n" " com.google.protobuf.ExtensionRegistry$lite$ registry) {\n", - "lite", HasDescriptorMethods(file_) ? "" : "Lite"); + "lite", + HasDescriptorMethods(file_, context_->EnforceLite()) ? "" : "Lite"); printer->Indent(); @@ -282,7 +285,7 @@ void FileGenerator::Generate(io::Printer* printer) { if (!MultipleJavaFiles(file_, immutable_api_)) { for (int i = 0; i < file_->enum_type_count(); i++) { - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, context_->EnforceLite())) { EnumGenerator(file_->enum_type(i), immutable_api_, context_.get()) .Generate(printer); } else { @@ -294,7 +297,7 @@ void FileGenerator::Generate(io::Printer* printer) { message_generators_[i]->GenerateInterface(printer); message_generators_[i]->Generate(printer); } - if (HasGenericServices(file_)) { + if (HasGenericServices(file_, context_->EnforceLite())) { for (int i = 0; i < file_->service_count(); i++) { google::protobuf::scoped_ptr generator( generator_factory_->NewServiceGenerator(file_->service(i))); @@ -309,14 +312,18 @@ void FileGenerator::Generate(io::Printer* printer) { extension_generators_[i]->Generate(printer); } - // Static variables. + // Static variables. We'd like them to be final if possible, but due to + // the JVM's 64k size limit on static blocks, we have to initialize some + // of them in methods; thus they cannot be final. + int static_block_bytecode_estimate = 0; for (int i = 0; i < file_->message_type_count(); i++) { - message_generators_[i]->GenerateStaticVariables(printer); + message_generators_[i]->GenerateStaticVariables( + printer, &static_block_bytecode_estimate); } printer->Print("\n"); - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, context_->EnforceLite())) { if (immutable_api_) { GenerateDescriptorInitializationCodeForImmutable(printer); } else { @@ -358,9 +365,11 @@ void FileGenerator::GenerateDescriptorInitializationCodeForImmutable( " getDescriptor() {\n" " return descriptor;\n" "}\n" - "private static com.google.protobuf.Descriptors.FileDescriptor\n" + "private static $final$ com.google.protobuf.Descriptors.FileDescriptor\n" " descriptor;\n" - "static {\n"); + "static {\n", + // TODO(dweis): Mark this as final. + "final", ""); printer->Indent(); SharedCodeGenerator shared_code_generator(file_); @@ -453,7 +462,7 @@ void FileGenerator::GenerateDescriptorInitializationCodeForMutable(io::Printer* " getDescriptor() {\n" " return descriptor;\n" "}\n" - "private static com.google.protobuf.Descriptors.FileDescriptor\n" + "private static final com.google.protobuf.Descriptors.FileDescriptor\n" " descriptor;\n" "static {\n"); printer->Indent(); @@ -549,7 +558,7 @@ void FileGenerator::GenerateSiblings(const string& package_dir, vector* file_list) { if (MultipleJavaFiles(file_, immutable_api_)) { for (int i = 0; i < file_->enum_type_count(); i++) { - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, context_->EnforceLite())) { EnumGenerator generator(file_->enum_type(i), immutable_api_, context_.get()); GenerateSibling(package_dir, java_package_, @@ -582,7 +591,7 @@ void FileGenerator::GenerateSiblings(const string& package_dir, message_generators_[i].get(), &MessageGenerator::Generate); } - if (HasGenericServices(file_)) { + if (HasGenericServices(file_, context_->EnforceLite())) { for (int i = 0; i < file_->service_count(); i++) { google::protobuf::scoped_ptr generator( generator_factory_->NewServiceGenerator(file_->service(i))); diff --git a/src/google/protobuf/compiler/java/java_file.h b/src/google/protobuf/compiler/java/java_file.h index 080b34241..7dbeb94ea 100644 --- a/src/google/protobuf/compiler/java/java_file.h +++ b/src/google/protobuf/compiler/java/java_file.h @@ -67,7 +67,8 @@ namespace java { class FileGenerator { public: - FileGenerator(const FileDescriptor* file, bool immutable_api = true); + FileGenerator(const FileDescriptor* file, bool immutable_api = true, + bool enforce_lite = false); ~FileGenerator(); // Checks for problems that would otherwise lead to cryptic compile errors. diff --git a/src/google/protobuf/compiler/java/java_generator.cc b/src/google/protobuf/compiler/java/java_generator.cc index 6c6f72861..a46c7fc41 100644 --- a/src/google/protobuf/compiler/java/java_generator.cc +++ b/src/google/protobuf/compiler/java/java_generator.cc @@ -75,6 +75,7 @@ bool JavaGenerator::Generate(const FileDescriptor* file, bool generate_immutable_code = false; bool generate_mutable_code = false; bool generate_shared_code = false; + bool enforce_lite = false; for (int i = 0; i < options.size(); i++) { if (options[i].first == "output_list_file") { output_list_file = options[i].second; @@ -84,12 +85,21 @@ bool JavaGenerator::Generate(const FileDescriptor* file, generate_mutable_code = true; } else if (options[i].first == "shared") { generate_shared_code = true; + } else if (options[i].first == "lite") { + // When set, the protoc will generate the current files and all the + // transitive dependencies as lite runtime. + enforce_lite = true; } else { *error = "Unknown generator option: " + options[i].first; return false; } } + if (enforce_lite && generate_mutable_code) { + *error = "lite runtime generator option cannot be used with mutable API."; + return false; + } + // By default we generate immutable code and shared code for immutable API. if (!generate_immutable_code && !generate_mutable_code && !generate_shared_code) { @@ -105,10 +115,12 @@ bool JavaGenerator::Generate(const FileDescriptor* file, vector file_generators; if (generate_immutable_code) { - file_generators.push_back(new FileGenerator(file, /* immutable = */ true)); + file_generators.push_back( + new FileGenerator(file, /* immutable = */ true, enforce_lite)); } if (generate_mutable_code) { - file_generators.push_back(new FileGenerator(file, /* mutable = */ false)); + file_generators.push_back( + new FileGenerator(file, /* mutable = */ false, enforce_lite)); } for (int i = 0; i < file_generators.size(); ++i) { if (!file_generators[i]->Validate(error)) { diff --git a/src/google/protobuf/compiler/java/java_generator_factory.cc b/src/google/protobuf/compiler/java/java_generator_factory.cc index 92ef851b8..3218b4107 100644 --- a/src/google/protobuf/compiler/java/java_generator_factory.cc +++ b/src/google/protobuf/compiler/java/java_generator_factory.cc @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -58,17 +59,20 @@ ImmutableGeneratorFactory::~ImmutableGeneratorFactory() {} MessageGenerator* ImmutableGeneratorFactory::NewMessageGenerator( const Descriptor* descriptor) const { - if (descriptor->file()->options().optimize_for() == - FileOptions::LITE_RUNTIME) { - return new ImmutableMessageLiteGenerator(descriptor, context_); - } else { + if (HasDescriptorMethods(descriptor, context_->EnforceLite())) { return new ImmutableMessageGenerator(descriptor, context_); + } else { + return new ImmutableMessageLiteGenerator(descriptor, context_); } } ExtensionGenerator* ImmutableGeneratorFactory::NewExtensionGenerator( const FieldDescriptor* descriptor) const { - return new ImmutableExtensionGenerator(descriptor, context_); + if (HasDescriptorMethods(descriptor->file(), context_->EnforceLite())) { + return new ImmutableExtensionGenerator(descriptor, context_); + } else { + return new ImmutableExtensionLiteGenerator(descriptor, context_); + } } ServiceGenerator* ImmutableGeneratorFactory::NewServiceGenerator( diff --git a/src/google/protobuf/compiler/java/java_helpers.h b/src/google/protobuf/compiler/java/java_helpers.h index 5392d6d72..c850423e7 100644 --- a/src/google/protobuf/compiler/java/java_helpers.h +++ b/src/google/protobuf/compiler/java/java_helpers.h @@ -170,44 +170,41 @@ inline string ImmutableDefaultValue(const FieldDescriptor* field, } bool IsDefaultValueJavaDefault(const FieldDescriptor* field); -// Does this message class have generated parsing, serialization, and other -// standard methods for which reflection-based fallback implementations exist? -inline bool HasGeneratedMethods(const Descriptor* descriptor) { - return descriptor->file()->options().optimize_for() != - FileOptions::CODE_SIZE; -} - // Does this message have specialized equals() and hashCode() methods? inline bool HasEqualsAndHashCode(const Descriptor* descriptor) { return descriptor->file()->options().java_generate_equals_and_hash(); } // Does this message class have descriptor and reflection methods? -inline bool HasDescriptorMethods(const Descriptor* descriptor) { - return descriptor->file()->options().optimize_for() != - FileOptions::LITE_RUNTIME; +inline bool HasDescriptorMethods(const Descriptor* descriptor, + bool enforce_lite) { + return !enforce_lite && + descriptor->file()->options().optimize_for() != + FileOptions::LITE_RUNTIME; } -inline bool HasDescriptorMethods(const EnumDescriptor* descriptor) { - return descriptor->file()->options().optimize_for() != - FileOptions::LITE_RUNTIME; +inline bool HasDescriptorMethods(const EnumDescriptor* descriptor, + bool enforce_lite) { + return !enforce_lite && + descriptor->file()->options().optimize_for() != + FileOptions::LITE_RUNTIME; } -inline bool HasDescriptorMethods(const FileDescriptor* descriptor) { - return descriptor->options().optimize_for() != - FileOptions::LITE_RUNTIME; +inline bool HasDescriptorMethods(const FileDescriptor* descriptor, + bool enforce_lite) { + return !enforce_lite && + descriptor->options().optimize_for() != FileOptions::LITE_RUNTIME; } // Should we generate generic services for this file? -inline bool HasGenericServices(const FileDescriptor *file) { +inline bool HasGenericServices(const FileDescriptor *file, bool enforce_lite) { return file->service_count() > 0 && - file->options().optimize_for() != FileOptions::LITE_RUNTIME && + HasDescriptorMethods(file, enforce_lite) && file->options().java_generic_services(); } -inline bool IsLazy(const FieldDescriptor* descriptor) { +inline bool IsLazy(const FieldDescriptor* descriptor, bool enforce_lite) { // Currently, the proto-lite version suports lazy field. // TODO(niwasaki): Support lazy fields also for other proto runtimes. - if (descriptor->file()->options().optimize_for() != - FileOptions::LITE_RUNTIME) { + if (HasDescriptorMethods(descriptor->file(), enforce_lite)) { return false; } return descriptor->options().lazy(); diff --git a/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc index 283ba1d3b..62f39302e 100644 --- a/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc @@ -182,18 +182,17 @@ GenerateInitializationCode(io::Printer* printer) const { } void ImmutableLazyMessageFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { +GenerateVisitCode(io::Printer* printer) const { printer->Print(variables_, - "if (other.has$capitalized_name$()) {\n" - " $name$_.merge(other.$name$_);\n" - " $set_has_field_bit_message$;\n" - "}\n"); + "$name$_ = visitor.visitLazyMessage(\n" + " has$capitalized_name$(), $name$_,\n" + " other.has$capitalized_name$(), other.$name$_);\n"); } void ImmutableLazyMessageFieldLiteGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, - "$name$_.setByteString(input.readBytes(), extensionRegistry);\n"); + "$name$_.mergeFrom(input, extensionRegistry);\n"); printer->Print(variables_, "$set_has_field_bit_message$;\n"); } @@ -362,14 +361,12 @@ GenerateBuilderMembers(io::Printer* printer) const { } void ImmutableLazyMessageOneofFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { +GenerateVisitCode(io::Printer* printer) const { printer->Print(variables_, - "if (!($has_oneof_case_message$)) {\n" - " $oneof_name$_ = new $lazy_type$();\n" - "}\n" - "(($lazy_type$) $oneof_name$_).merge(\n" - " ($lazy_type$) other.$oneof_name$_);\n" - "$set_oneof_case_message$;\n"); + "$oneof_name$_ = visitor.visitOneofLazyMessage(\n" + " $has_oneof_case_message$,\n" + " ($lazy_type$) $oneof_name$_,\n" + " ($lazy_type$) other.$oneof_name$_);\n"); } void ImmutableLazyMessageOneofFieldLiteGenerator:: @@ -378,8 +375,7 @@ GenerateParsingCode(io::Printer* printer) const { "if (!($has_oneof_case_message$)) {\n" " $oneof_name$_ = new $lazy_type$();\n" "}\n" - "(($lazy_type$) $oneof_name$_).setByteString(\n" - " input.readBytes(), extensionRegistry);\n" + "(($lazy_type$) $oneof_name$_).mergeFrom(input, extensionRegistry);\n" "$set_oneof_case_message$;\n"); } @@ -464,7 +460,8 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private void ensure$capitalized_name$IsMutable() {\n" " if (!$is_mutable$) {\n" - " $name$_ = newProtobufList($name$_);\n" + " $name$_ =\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" " }\n" "}\n" "\n"); @@ -679,7 +676,8 @@ void RepeatedImmutableLazyMessageFieldLiteGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, "if (!$is_mutable$) {\n" - " $name$_ = newProtobufList();\n" + " $name$_ =\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" "}\n" "$name$_.add(new com.google.protobuf.LazyFieldLite(\n" " extensionRegistry, input.readBytes()));\n"); diff --git a/src/google/protobuf/compiler/java/java_lazy_message_field_lite.h b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.h index e85ec0f35..47ebeb49a 100644 --- a/src/google/protobuf/compiler/java/java_lazy_message_field_lite.h +++ b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.h @@ -63,7 +63,7 @@ class ImmutableLazyMessageFieldLiteGenerator void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; @@ -82,7 +82,7 @@ class ImmutableLazyMessageOneofFieldLiteGenerator void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/java/java_map_field.cc b/src/google/protobuf/compiler/java/java_map_field.cc index 3e035c89b..2a551ca47 100644 --- a/src/google/protobuf/compiler/java/java_map_field.cc +++ b/src/google/protobuf/compiler/java/java_map_field.cc @@ -79,9 +79,10 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, int builderBitIndex, const FieldGeneratorInfo* info, - ClassNameResolver* name_resolver, + Context* context, map* variables) { SetCommonFieldVariables(descriptor, info, variables); + ClassNameResolver* name_resolver = context->GetNameResolver(); (*variables)["type"] = name_resolver->GetImmutableClassName(descriptor->message_type()); @@ -123,8 +124,7 @@ void SetMessageVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + (*variables)["on_changed"] = "onChanged();"; // For repeated fields, one bit is used for whether the array is immutable // in the parsing constructor. @@ -135,18 +135,12 @@ void SetMessageVariables(const FieldDescriptor* descriptor, (*variables)["default_entry"] = (*variables)["capitalized_name"] + "DefaultEntryHolder.defaultEntry"; - if (HasDescriptorMethods(descriptor->file())) { - (*variables)["lite"] = ""; - (*variables)["map_field_parameter"] = (*variables)["default_entry"]; - (*variables)["descriptor"] = - name_resolver->GetImmutableClassName(descriptor->file()) + - ".internal_" + UniqueFileScopeIdentifier(descriptor->message_type()) + - "_descriptor, "; - } else { - (*variables)["lite"] = "Lite"; - (*variables)["map_field_parameter"] = ""; - (*variables)["descriptor"] = ""; - } + (*variables)["lite"] = ""; + (*variables)["map_field_parameter"] = (*variables)["default_entry"]; + (*variables)["descriptor"] = + name_resolver->GetImmutableClassName(descriptor->file()) + + ".internal_" + UniqueFileScopeIdentifier(descriptor->message_type()) + + "_descriptor, "; } } // namespace @@ -159,7 +153,7 @@ ImmutableMapFieldGenerator(const FieldDescriptor* descriptor, : descriptor_(descriptor), name_resolver_(context->GetNameResolver()) { SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, context->GetFieldGeneratorInfo(descriptor), - name_resolver_, &variables_); + context, &variables_); } ImmutableMapFieldGenerator:: @@ -221,7 +215,7 @@ GenerateMembers(io::Printer* printer) const { " if ($name$_ == null) {\n" " return com.google.protobuf.MapField$lite$.emptyMapField(\n" " $map_field_parameter$);\n" - " }\n" + " }\n" " return $name$_;\n" "}\n"); if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { @@ -276,7 +270,7 @@ GenerateBuilderMembers(io::Printer* printer) const { " if ($name$_ == null) {\n" " return com.google.protobuf.MapField$lite$.emptyMapField(\n" " $map_field_parameter$);\n" - " }\n" + " }\n" " return $name$_;\n" "}\n" "private com.google.protobuf.MapField$lite$<$type_parameters$>\n" @@ -424,7 +418,7 @@ GenerateParsingCode(io::Printer* printer) const { "$name$ = $default_entry$.getParserForType().parseFrom(bytes);\n"); printer->Print( variables_, - "if ($value_enum_type$.valueOf($name$.getValue()) == null) {\n" + "if ($value_enum_type$.forNumber($name$.getValue()) == null) {\n" " unknownFields.mergeLengthDelimitedField($number$, bytes);\n" "} else {\n" " $name$_.getMutableMap().put($name$.getKey(), $name$.getValue());\n" diff --git a/src/google/protobuf/compiler/java/java_map_field_lite.cc b/src/google/protobuf/compiler/java/java_map_field_lite.cc index d2039403e..b80d41390 100644 --- a/src/google/protobuf/compiler/java/java_map_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_map_field_lite.cc @@ -79,10 +79,11 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, int builderBitIndex, const FieldGeneratorInfo* info, - ClassNameResolver* name_resolver, + Context* context, map* variables) { SetCommonFieldVariables(descriptor, info, variables); + ClassNameResolver* name_resolver = context->GetNameResolver(); (*variables)["type"] = name_resolver->GetImmutableClassName(descriptor->message_type()); const FieldDescriptor* key = KeyField(descriptor); @@ -123,8 +124,6 @@ void SetMessageVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; (*variables)["default_entry"] = (*variables)["capitalized_name"] + "DefaultEntryHolder.defaultEntry"; @@ -142,7 +141,7 @@ ImmutableMapFieldLiteGenerator(const FieldDescriptor* descriptor, : descriptor_(descriptor), name_resolver_(context->GetNameResolver()) { SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, context->GetFieldGeneratorInfo(descriptor), - name_resolver_, &variables_); + context, &variables_); } ImmutableMapFieldLiteGenerator:: @@ -375,10 +374,10 @@ GenerateInitializationCode(io::Printer* printer) const { } void ImmutableMapFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { +GenerateVisitCode(io::Printer* printer) const { printer->Print( variables_, - "internalGetMutable$capitalized_name$().mergeFrom(\n" + "$name$_ = visitor.visitMap(internalGetMutable$capitalized_name$(),\n" " other.internalGet$capitalized_name$());\n"); } @@ -404,7 +403,7 @@ GenerateParsingCode(io::Printer* printer) const { "$name$ = $default_entry$.getParserForType().parseFrom(bytes);\n"); printer->Print( variables_, - "if ($value_enum_type$.valueOf($name$.getValue()) == null) {\n" + "if ($value_enum_type$.forNumber($name$.getValue()) == null) {\n" " super.mergeLengthDelimitedField($number$, bytes);\n" "} else {\n" " $name$_.getMutableMap().put($name$.getKey(), $name$.getValue());\n" diff --git a/src/google/protobuf/compiler/java/java_map_field_lite.h b/src/google/protobuf/compiler/java/java_map_field_lite.h index a09cd5363..555b5c5bd 100644 --- a/src/google/protobuf/compiler/java/java_map_field_lite.h +++ b/src/google/protobuf/compiler/java/java_map_field_lite.h @@ -52,7 +52,7 @@ class ImmutableMapFieldLiteGenerator : public ImmutableFieldLiteGenerator { void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateParsingDoneCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc index 22a70c326..4c474a48a 100644 --- a/src/google/protobuf/compiler/java/java_message.cc +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -89,19 +89,20 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor) MessageGenerator::~MessageGenerator() {} // =================================================================== -// TODO(api): Move this class to a separate immutable_message.cc file. ImmutableMessageGenerator::ImmutableMessageGenerator( const Descriptor* descriptor, Context* context) : MessageGenerator(descriptor), context_(context), name_resolver_(context->GetNameResolver()), field_generators_(descriptor, context_) { - GOOGLE_CHECK_NE( - FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); + GOOGLE_CHECK(HasDescriptorMethods(descriptor->file(), context->EnforceLite())) + << "Generator factory error: A non-lite message generator is used to " + "generate lite messages."; } ImmutableMessageGenerator::~ImmutableMessageGenerator() {} -void ImmutableMessageGenerator::GenerateStaticVariables(io::Printer* printer) { +void ImmutableMessageGenerator::GenerateStaticVariables( + io::Printer* printer, int* bytecode_estimate) { // Because descriptor.proto (com.google.protobuf.DescriptorProtos) is // used in the construction of descriptors, we have a tricky bootstrapping // problem. To help control static initialization order, we make sure all @@ -124,23 +125,29 @@ void ImmutableMessageGenerator::GenerateStaticVariables(io::Printer* printer) { } else { vars["private"] = "private "; } + if (*bytecode_estimate <= kMaxStaticSize) { + vars["final"] = "final "; + } else { + vars["final"] = ""; + } // The descriptor for this type. printer->Print(vars, // TODO(teboring): final needs to be added back. The way to fix it is to // generate methods that can construct the types, and then still declare the // types, and then init them in clinit with the new method calls. - "$private$static com.google.protobuf.Descriptors.Descriptor\n" + "$private$static $final$com.google.protobuf.Descriptors.Descriptor\n" " internal_$identifier$_descriptor;\n"); + *bytecode_estimate += 30; // And the FieldAccessorTable. - GenerateFieldAccessorTable(printer); + GenerateFieldAccessorTable(printer, bytecode_estimate); // Generate static members for all nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { // TODO(kenton): Reuse MessageGenerator objects? ImmutableMessageGenerator(descriptor_->nested_type(i), context_) - .GenerateStaticVariables(printer); + .GenerateStaticVariables(printer, bytecode_estimate); } } @@ -183,7 +190,7 @@ int ImmutableMessageGenerator::GenerateStaticVariableInitializers( } void ImmutableMessageGenerator:: -GenerateFieldAccessorTable(io::Printer* printer) { +GenerateFieldAccessorTable(io::Printer* printer, int* bytecode_estimate) { map vars; vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); if (MultipleJavaFiles(descriptor_->file(), /* immutable = */ true)) { @@ -193,10 +200,19 @@ GenerateFieldAccessorTable(io::Printer* printer) { } else { vars["private"] = "private "; } + if (*bytecode_estimate <= kMaxStaticSize) { + vars["final"] = "final "; + } else { + vars["final"] = ""; + } printer->Print(vars, - "$private$static\n" + "$private$static $final$\n" " com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" " internal_$identifier$_fieldAccessorTable;\n"); + + // 6 bytes per field and oneof + *bytecode_estimate += 10 + 6 * descriptor_->field_count() + + 6 * descriptor_->oneof_decl_count(); } int ImmutableMessageGenerator:: @@ -342,7 +358,7 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { printer->Print( "}\n"); - if (HasGeneratedMethods(descriptor_)) { + if (context_->HasGeneratedMethods(descriptor_)) { GenerateParsingConstructor(printer); } @@ -408,12 +424,20 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { "cap_oneof_name", ToUpper(vars["oneof_name"])); printer->Print(vars, - "private int value = 0;\n" + "private final int value;\n" "private $oneof_capitalized_name$Case(int value) {\n" " this.value = value;\n" "}\n"); printer->Print(vars, + "/**\n" + " * @deprecated Use {@link #forNumber(int)} instead.\n" + " */\n" + "@java.lang.Deprecated\n" "public static $oneof_capitalized_name$Case valueOf(int value) {\n" + " return forNumber(value);\n" + "}\n" + "\n" + "public static $oneof_capitalized_name$Case forNumber(int value) {\n" " switch (value) {\n"); for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); @@ -426,8 +450,7 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { } printer->Print( " case 0: return $cap_oneof_name$_NOT_SET;\n" - " default: throw new java.lang.IllegalArgumentException(\n" - " \"Value is undefined for this oneof enum.\");\n" + " default: return null;\n" " }\n" "}\n" "public int getNumber() {\n" @@ -440,7 +463,7 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { printer->Print(vars, "public $oneof_capitalized_name$Case\n" "get$oneof_capitalized_name$Case() {\n" - " return $oneof_capitalized_name$Case.valueOf(\n" + " return $oneof_capitalized_name$Case.forNumber(\n" " $oneof_name$Case_);\n" "}\n" "\n"); @@ -459,7 +482,7 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { printer->Print("\n"); } - if (HasGeneratedMethods(descriptor_)) { + if (context_->HasGeneratedMethods(descriptor_)) { GenerateIsInitialized(printer); GenerateMessageSerializationMethods(printer); } @@ -664,34 +687,40 @@ GenerateParseFromMethods(io::Printer* printer) { "}\n" "public static $classname$ parseFrom(java.io.InputStream input)\n" " throws java.io.IOException {\n" - " return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input);" + " return com.google.protobuf.GeneratedMessage\n" + " .parseWithIOException(PARSER, input);\n" "}\n" "public static $classname$ parseFrom(\n" " java.io.InputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input, extensionRegistry);" + " return com.google.protobuf.GeneratedMessage\n" + " .parseWithIOException(PARSER, input, extensionRegistry);\n" "}\n" "public static $classname$ parseDelimitedFrom(java.io.InputStream input)\n" " throws java.io.IOException {\n" - " return com.google.protobuf.GeneratedMessage.parseDelimitedWithIOException(PARSER, input);" + " return com.google.protobuf.GeneratedMessage\n" + " .parseDelimitedWithIOException(PARSER, input);\n" "}\n" "public static $classname$ parseDelimitedFrom(\n" " java.io.InputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return com.google.protobuf.GeneratedMessage.parseDelimitedWithIOException(PARSER, input, extensionRegistry);" + " return com.google.protobuf.GeneratedMessage\n" + " .parseDelimitedWithIOException(PARSER, input, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.CodedInputStream input)\n" " throws java.io.IOException {\n" - " return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input);" + " return com.google.protobuf.GeneratedMessage\n" + " .parseWithIOException(PARSER, input);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.CodedInputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return com.google.protobuf.GeneratedMessage.parseWithIOException(PARSER, input, extensionRegistry);" + " return com.google.protobuf.GeneratedMessage\n" + " .parseWithIOException(PARSER, input, extensionRegistry);\n" "}\n" "\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); @@ -1049,22 +1078,52 @@ GenerateEqualsAndHashCode(io::Printer* printer) { printer->Print("hash = (19 * hash) + getDescriptorForType().hashCode();\n"); + // hashCode non-oneofs. for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); - const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); - bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); - if (check_has_bits) { - printer->Print( - "if (has$name$()) {\n", - "name", info->capitalized_name); - printer->Indent(); - } - field_generators_.get(field).GenerateHashCode(printer); - if (check_has_bits) { - printer->Outdent(); - printer->Print("}\n"); + if (field->containing_oneof() == NULL) { + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); + if (check_has_bits) { + printer->Print( + "if (has$name$()) {\n", + "name", info->capitalized_name); + printer->Indent(); + } + field_generators_.get(field).GenerateHashCode(printer); + if (check_has_bits) { + printer->Outdent(); + printer->Print("}\n"); + } } } + + // hashCode oneofs. + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "switch ($oneof_name$Case_) {\n", + "oneof_name", + context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name); + printer->Indent(); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + printer->Print( + "case $field_number$:\n", + "field_number", + SimpleItoa(field->number())); + printer->Indent(); + field_generators_.get(field).GenerateHashCode(printer); + printer->Print("break;\n"); + printer->Outdent(); + } + printer->Print( + "case 0:\n" + "default:\n"); + printer->Outdent(); + printer->Print("}\n"); + } + if (descriptor_->extension_range_count() > 0) { printer->Print( "hash = hashFields(hash, getExtensionFields());\n"); @@ -1105,7 +1164,8 @@ GenerateParsingConstructor(io::Printer* printer) { printer->Print( "private $classname$(\n" " com.google.protobuf.CodedInputStream input,\n" - " com.google.protobuf.ExtensionRegistryLite extensionRegistry) {\n", + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n", "classname", descriptor_->name()); printer->Indent(); @@ -1165,7 +1225,8 @@ GenerateParsingConstructor(io::Printer* printer) { "default: {\n" " if (!input.skipField(tag)) {\n" " done = true;\n" // it's an endgroup tag - " }\n" + " }\n"); + printer->Print( " break;\n" "}\n"); } @@ -1215,10 +1276,10 @@ GenerateParsingConstructor(io::Printer* printer) { printer->Outdent(); printer->Print( "} catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" - " throw new RuntimeException(e.setUnfinishedMessage(this));\n" + " throw e.setUnfinishedMessage(this);\n" "} catch (java.io.IOException e) {\n" - " throw new RuntimeException(new com.google.protobuf.InvalidProtocolBufferException(e)\n" - " .setUnfinishedMessage(this));\n" + " throw new com.google.protobuf.InvalidProtocolBufferException(\n" + " e).setUnfinishedMessage(this);\n" "} finally {\n"); printer->Indent(); @@ -1260,20 +1321,9 @@ void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) { " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n", "classname", descriptor_->name()); - if (HasGeneratedMethods(descriptor_)) { - // The parsing constructor throws an InvalidProtocolBufferException via a - // RuntimeException to aid in method pruning. We unwrap it here. + if (context_->HasGeneratedMethods(descriptor_)) { printer->Print( - " try {\n" - " return new $classname$(input, extensionRegistry);\n" - " } catch (RuntimeException e) {\n" - " if (e.getCause() instanceof\n" - " com.google.protobuf.InvalidProtocolBufferException) {\n" - " throw (com.google.protobuf.InvalidProtocolBufferException)\n" - " e.getCause();\n" - " }\n" - " throw e;\n" - " }\n", + " return new $classname$(input, extensionRegistry);\n", "classname", descriptor_->name()); } else { // When parsing constructor isn't generated, use builder to parse @@ -1328,14 +1378,39 @@ void ImmutableMessageGenerator::GenerateInitializers(io::Printer* printer) { void ImmutableMessageGenerator::GenerateAnyMethods(io::Printer* printer) { printer->Print( "private static String getTypeUrl(\n" + " java.lang.String typeUrlPrefix,\n" " com.google.protobuf.Descriptors.Descriptor descriptor) {\n" - " return \"type.googleapis.com/\" + descriptor.getFullName();\n" + " return typeUrlPrefix.endsWith(\"/\")\n" + " ? typeUrlPrefix + descriptor.getFullName()\n" + " : typeUrlPrefix + \"/\" + descriptor.getFullName();\n" + "}\n" + "\n" + "private static String getTypeNameFromTypeUrl(\n" + " java.lang.String typeUrl) {\n" + " int pos = typeUrl.lastIndexOf('/');\n" + " return pos == -1 ? \"\" : typeUrl.substring(pos + 1);\n" "}\n" "\n" "public static Any pack(\n" " T message) {\n" " return Any.newBuilder()\n" - " .setTypeUrl(getTypeUrl(message.getDescriptorForType()))\n" + " .setTypeUrl(getTypeUrl(\"type.googleapis.com\",\n" + " message.getDescriptorForType()))\n" + " .setValue(message.toByteString())\n" + " .build();\n" + "}\n" + "\n" + "/**\n" + " * Packs a message uisng the given type URL prefix. The type URL will\n" + " * be constructed by concatenating the message type's full name to the\n" + " * prefix with an optional \"/\" separator if the prefix doesn't end\n" + " * with \"/\" already.\n" + " */\n" + "public static Any pack(\n" + " T message, java.lang.String typeUrlPrefix) {\n" + " return Any.newBuilder()\n" + " .setTypeUrl(getTypeUrl(typeUrlPrefix,\n" + " message.getDescriptorForType()))\n" " .setValue(message.toByteString())\n" " .build();\n" "}\n" @@ -1344,8 +1419,8 @@ void ImmutableMessageGenerator::GenerateAnyMethods(io::Printer* printer) { " java.lang.Class clazz) {\n" " T defaultInstance =\n" " com.google.protobuf.Internal.getDefaultInstance(clazz);\n" - " return getTypeUrl().equals(\n" - " getTypeUrl(defaultInstance.getDescriptorForType()));\n" + " return getTypeNameFromTypeUrl(getTypeUrl()).equals(\n" + " defaultInstance.getDescriptorForType().getFullName());\n" "}\n" "\n" "private volatile com.google.protobuf.Message cachedUnpackValue;\n" diff --git a/src/google/protobuf/compiler/java/java_message.h b/src/google/protobuf/compiler/java/java_message.h index be5bfb075..e9fc57c26 100644 --- a/src/google/protobuf/compiler/java/java_message.h +++ b/src/google/protobuf/compiler/java/java_message.h @@ -56,6 +56,8 @@ namespace protobuf { namespace compiler { namespace java { +static const int kMaxStaticSize = 1 << 15; // aka 32k + class MessageGenerator { public: explicit MessageGenerator(const Descriptor* descriptor); @@ -64,7 +66,8 @@ class MessageGenerator { // All static variables have to be declared at the top-level of the file // so that we can control initialization order, which is important for // DescriptorProto bootstrapping to work. - virtual void GenerateStaticVariables(io::Printer* printer) = 0; + virtual void GenerateStaticVariables( + io::Printer* printer, int* bytecode_estimate) = 0; // Output code which initializes the static variables generated by // GenerateStaticVariables(). Returns an estimate of bytecode size. @@ -96,14 +99,15 @@ class ImmutableMessageGenerator : public MessageGenerator { virtual void Generate(io::Printer* printer); virtual void GenerateInterface(io::Printer* printer); virtual void GenerateExtensionRegistrationCode(io::Printer* printer); - virtual void GenerateStaticVariables(io::Printer* printer); + virtual void GenerateStaticVariables( + io::Printer* printer, int* bytecode_estimate); // Returns an estimate of the number of bytes the printed code will compile to virtual int GenerateStaticVariableInitializers(io::Printer* printer); private: - void GenerateFieldAccessorTable(io::Printer* printer); + void GenerateFieldAccessorTable(io::Printer* printer, int* bytecode_estimate); // Returns an estimate of the number of bytes the printed code will compile to int GenerateFieldAccessorTableInitializer(io::Printer* printer); diff --git a/src/google/protobuf/compiler/java/java_message_builder.cc b/src/google/protobuf/compiler/java/java_message_builder.cc index 5d5350349..b3e9e986c 100644 --- a/src/google/protobuf/compiler/java/java_message_builder.cc +++ b/src/google/protobuf/compiler/java/java_message_builder.cc @@ -81,8 +81,9 @@ MessageBuilderGenerator::MessageBuilderGenerator( : descriptor_(descriptor), context_(context), name_resolver_(context->GetNameResolver()), field_generators_(descriptor, context_) { - GOOGLE_CHECK_NE( - FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); + GOOGLE_CHECK(HasDescriptorMethods(descriptor->file(), context->EnforceLite())) + << "Generator factory error: A non-lite message generator is used to " + "generate lite messages."; } MessageBuilderGenerator::~MessageBuilderGenerator() {} @@ -113,7 +114,7 @@ Generate(io::Printer* printer) { GenerateDescriptorMethods(printer); GenerateCommonBuilderMethods(printer); - if (HasGeneratedMethods(descriptor_)) { + if (context_->HasGeneratedMethods(descriptor_)) { GenerateIsInitialized(printer); GenerateBuilderParsingMethods(printer); } @@ -134,7 +135,7 @@ Generate(io::Printer* printer) { printer->Print(vars, "public $oneof_capitalized_name$Case\n" " get$oneof_capitalized_name$Case() {\n" - " return $oneof_capitalized_name$Case.valueOf(\n" + " return $oneof_capitalized_name$Case.forNumber(\n" " $oneof_name$Case_);\n" "}\n" "\n" @@ -439,7 +440,7 @@ GenerateCommonBuilderMethods(io::Printer* printer) { // ----------------------------------------------------------------- - if (HasGeneratedMethods(descriptor_)) { + if (context_->HasGeneratedMethods(descriptor_)) { printer->Print( "public Builder mergeFrom(com.google.protobuf.Message other) {\n" " if (other instanceof $classname$) {\n" diff --git a/src/google/protobuf/compiler/java/java_message_builder_lite.cc b/src/google/protobuf/compiler/java/java_message_builder_lite.cc index 8719d00d1..dd429dc93 100644 --- a/src/google/protobuf/compiler/java/java_message_builder_lite.cc +++ b/src/google/protobuf/compiler/java/java_message_builder_lite.cc @@ -81,8 +81,9 @@ MessageBuilderLiteGenerator::MessageBuilderLiteGenerator( : descriptor_(descriptor), context_(context), name_resolver_(context->GetNameResolver()), field_generators_(descriptor, context_) { - GOOGLE_CHECK_EQ( - FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); + GOOGLE_CHECK(!HasDescriptorMethods(descriptor->file(), context->EnforceLite())) + << "Generator factory error: A lite message generator is used to " + "generate non-lite messages."; } MessageBuilderLiteGenerator::~MessageBuilderLiteGenerator() {} @@ -148,20 +149,6 @@ Generate(io::Printer* printer) { .GenerateBuilderMembers(printer); } - if (!PreserveUnknownFields(descriptor_)) { - printer->Print( - "public final Builder setUnknownFields(\n" - " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" - " return this;\n" - "}\n" - "\n" - "public final Builder mergeUnknownFields(\n" - " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" - " return this;\n" - "}\n" - "\n"); - } - printer->Print( "\n" "// @@protoc_insertion_point(builder_scope:$full_name$)\n", diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc index b5f8e626f..455516f67 100644 --- a/src/google/protobuf/compiler/java/java_message_field.cc +++ b/src/google/protobuf/compiler/java/java_message_field.cc @@ -70,8 +70,7 @@ void SetMessageVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + (*variables)["on_changed"] = "onChanged();"; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. diff --git a/src/google/protobuf/compiler/java/java_message_field_lite.cc b/src/google/protobuf/compiler/java/java_message_field_lite.cc index 356520ec6..142818169 100644 --- a/src/google/protobuf/compiler/java/java_message_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_message_field_lite.cc @@ -70,8 +70,6 @@ void SetMessageVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -288,11 +286,9 @@ void ImmutableMessageFieldLiteGenerator:: GenerateInitializationCode(io::Printer* printer) const {} void ImmutableMessageFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { +GenerateVisitCode(io::Printer* printer) const { printer->Print(variables_, - "if (other.has$capitalized_name$()) {\n" - " merge$capitalized_name$(other.get$capitalized_name$());\n" - "}\n"); + "$name$_ = visitor.visitMessage($name$_, other.$name$_);\n"); } void ImmutableMessageFieldLiteGenerator:: @@ -302,11 +298,18 @@ GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { void ImmutableMessageFieldLiteGenerator:: GenerateParsingCode(io::Printer* printer) const { + // TODO(dweis): Update this code to avoid the builder allocation and instead + // only allocate a submessage that isn't made immutable. Rely on the top + // message calling makeImmutable once done to actually traverse the tree and + // finalize state. This will avoid: + // - transitive builder allocations + // - the extra transitive iteration for streamed fields + // - reallocations for copying repeated fields printer->Print(variables_, - "$type$.Builder subBuilder = null;\n" - "if ($is_field_present_message$) {\n" - " subBuilder = $name$_.toBuilder();\n" - "}\n"); + "$type$.Builder subBuilder = null;\n" + "if ($is_field_present_message$) {\n" + " subBuilder = $name$_.toBuilder();\n" + "}\n"); if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { printer->Print(variables_, @@ -506,9 +509,12 @@ GenerateBuilderMembers(io::Printer* printer) const { } void ImmutableMessageOneofFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { +GenerateVisitCode(io::Printer* printer) const { printer->Print(variables_, - "merge$capitalized_name$(other.get$capitalized_name$());\n"); + "$oneof_name$_ = visitor.visitOneofMessage(\n" + " $has_oneof_case_message$,\n" + " $oneof_name$_,\n" + " other.$oneof_name$_);\n"); } void ImmutableMessageOneofFieldLiteGenerator:: @@ -635,7 +641,8 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private void ensure$capitalized_name$IsMutable() {\n" " if (!$is_mutable$) {\n" - " $name$_ = newProtobufList($name$_);\n" + " $name$_ =\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" " }\n" "}\n" "\n"); @@ -853,22 +860,9 @@ GenerateInitializationCode(io::Printer* printer) const { } void RepeatedImmutableMessageFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { - // The code below does two optimizations (non-nested builder case): - // 1. If the other list is empty, there's nothing to do. This ensures we - // don't allocate a new array if we already have an immutable one. - // 2. If the other list is non-empty and our current list is empty, we can - // reuse the other list which is guaranteed to be immutable. +GenerateVisitCode(io::Printer* printer) const { printer->Print(variables_, - "if (!other.$name$_.isEmpty()) {\n" - " if ($name$_.isEmpty()) {\n" - " $name$_ = other.$name$_;\n" - " } else {\n" - " ensure$capitalized_name$IsMutable();\n" - " $name$_.addAll(other.$name$_);\n" - " }\n" - " $on_changed$\n" - "}\n"); + "$name$_= visitor.visitList($name$_, other.$name$_);\n"); } void RepeatedImmutableMessageFieldLiteGenerator:: @@ -881,7 +875,8 @@ void RepeatedImmutableMessageFieldLiteGenerator:: GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, "if (!$is_mutable$) {\n" - " $name$_ = newProtobufList();\n" + " $name$_ =\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" "}\n"); if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { diff --git a/src/google/protobuf/compiler/java/java_message_field_lite.h b/src/google/protobuf/compiler/java/java_message_field_lite.h index ae26c06aa..613215477 100644 --- a/src/google/protobuf/compiler/java/java_message_field_lite.h +++ b/src/google/protobuf/compiler/java/java_message_field_lite.h @@ -67,7 +67,7 @@ class ImmutableMessageFieldLiteGenerator : public ImmutableFieldLiteGenerator { void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateParsingDoneCode(io::Printer* printer) const; @@ -101,7 +101,7 @@ class ImmutableMessageOneofFieldLiteGenerator void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; @@ -125,7 +125,7 @@ class RepeatedImmutableMessageFieldLiteGenerator void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateParsingDoneCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/java/java_message_lite.cc b/src/google/protobuf/compiler/java/java_message_lite.cc index 94ed2c396..d4d2593a6 100644 --- a/src/google/protobuf/compiler/java/java_message_lite.cc +++ b/src/google/protobuf/compiler/java/java_message_lite.cc @@ -46,7 +46,7 @@ #include #include #include -#include +#include #include #include #include @@ -87,19 +87,20 @@ ImmutableMessageLiteGenerator::ImmutableMessageLiteGenerator( : MessageGenerator(descriptor), context_(context), name_resolver_(context->GetNameResolver()), field_generators_(descriptor, context_) { - GOOGLE_CHECK_EQ( - FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); + GOOGLE_CHECK(!HasDescriptorMethods(descriptor->file(), context->EnforceLite())) + << "Generator factory error: A lite message generator is used to " + "generate non-lite messages."; } ImmutableMessageLiteGenerator::~ImmutableMessageLiteGenerator() {} void ImmutableMessageLiteGenerator::GenerateStaticVariables( - io::Printer* printer) { + io::Printer* printer, int* bytecode_estimate) { // Generate static members for all nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { // TODO(kenton): Reuse MessageGenerator objects? ImmutableMessageLiteGenerator(descriptor_->nested_type(i), context_) - .GenerateStaticVariables(printer); + .GenerateStaticVariables(printer, bytecode_estimate); } } @@ -197,7 +198,8 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { } printer->Indent(); - GenerateParsingConstructor(printer); + + GenerateConstructor(printer); // Nested types for (int i = 0; i < descriptor_->enum_type_count(); i++) { @@ -259,12 +261,20 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { "cap_oneof_name", ToUpper(vars["oneof_name"])); printer->Print(vars, - "private int value = 0;\n" + "private final int value;\n" "private $oneof_capitalized_name$Case(int value) {\n" " this.value = value;\n" "}\n"); printer->Print(vars, + "/**\n" + " * @deprecated Use {@link #forNumber(int)} instead.\n" + " */\n" + "@java.lang.Deprecated\n" "public static $oneof_capitalized_name$Case valueOf(int value) {\n" + " return forNumber(value);\n" + "}\n" + "\n" + "public static $oneof_capitalized_name$Case forNumber(int value) {\n" " switch (value) {\n"); for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); @@ -277,8 +287,7 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { } printer->Print( " case 0: return $cap_oneof_name$_NOT_SET;\n" - " default: throw new java.lang.IllegalArgumentException(\n" - " \"Value is undefined for this oneof enum.\");\n" + " default: return null;\n" " }\n" "}\n" "public int getNumber() {\n" @@ -291,7 +300,7 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { printer->Print(vars, "public $oneof_capitalized_name$Case\n" "get$oneof_capitalized_name$Case() {\n" - " return $oneof_capitalized_name$Case.valueOf(\n" + " return $oneof_capitalized_name$Case.forNumber(\n" " $oneof_name$Case_);\n" "}\n" "\n" @@ -313,11 +322,6 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { GenerateMessageSerializationMethods(printer); - if (HasEqualsAndHashCode(descriptor_)) { - GenerateEqualsAndHashCode(printer); - } - - GenerateParseFromMethods(printer); GenerateBuilder(printer); @@ -334,16 +338,8 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { " com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n" " Object arg0, Object arg1) {\n" " switch (method) {\n" - " case PARSE_PARTIAL_FROM: {\n" - " return new $classname$(" - " (com.google.protobuf.CodedInputStream) arg0,\n" - " (com.google.protobuf.ExtensionRegistryLite) arg1);\n" - " }\n" - " case NEW_INSTANCE: {\n" - " return new $classname$(\n" - " com.google.protobuf.Internal.EMPTY_CODED_INPUT_STREAM,\n" - " com.google.protobuf.ExtensionRegistryLite\n" - " .getEmptyRegistry());\n" + " case NEW_MUTABLE_INSTANCE: {\n" + " return new $classname$();\n" " }\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); @@ -374,10 +370,18 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { printer->Print( "}\n" - "case MERGE_FROM: {\n"); + "case VISIT: {\n"); printer->Indent(); - GenerateDynamicMethodMergeFrom(printer); + GenerateDynamicMethodVisit(printer); + printer->Outdent(); + + printer->Print( + "}\n" + "case MERGE_FROM_STREAM: {\n"); + + printer->Indent(); + GenerateDynamicMethodMergeFromStream(printer); printer->Outdent(); printer->Print( @@ -425,11 +429,8 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { printer->Print( "static {\n" - " DEFAULT_INSTANCE = new $classname$(\n" - " com.google.protobuf.Internal\n" - " .EMPTY_CODED_INPUT_STREAM,\n" - " com.google.protobuf.ExtensionRegistryLite\n" - " .getEmptyRegistry());\n" + " DEFAULT_INSTANCE = new $classname$();\n" + " DEFAULT_INSTANCE.makeImmutable();\n" "}\n" "\n", "classname", descriptor_->name()); @@ -446,7 +447,7 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { // because the DEFAULT_INSTANCE is used by the extension to lazily retrieve // the outer class's FileDescriptor. for (int i = 0; i < descriptor_->extension_count(); i++) { - ImmutableExtensionGenerator(descriptor_->extension(i), context_) + ImmutableExtensionLiteGenerator(descriptor_->extension(i), context_) .Generate(printer); } @@ -557,9 +558,6 @@ GenerateMessageSerializationMethods(io::Printer* printer) { " return size;\n" "}\n" "\n"); - - printer->Print( - "private static final long serialVersionUID = 0L;\n"); } void ImmutableMessageLiteGenerator:: @@ -571,54 +569,62 @@ GenerateParseFromMethods(io::Printer* printer) { "public static $classname$ parseFrom(\n" " com.google.protobuf.ByteString data)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return parser().parseFrom(data);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, data);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.ByteString data,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return parser().parseFrom(data, extensionRegistry);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, data, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(byte[] data)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return parser().parseFrom(data);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, data);\n" "}\n" "public static $classname$ parseFrom(\n" " byte[] data,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return parser().parseFrom(data, extensionRegistry);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, data, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(java.io.InputStream input)\n" " throws java.io.IOException {\n" - " return parser().parseFrom(input);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, input);\n" "}\n" "public static $classname$ parseFrom(\n" " java.io.InputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return parser().parseFrom(input, extensionRegistry);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, input, extensionRegistry);\n" "}\n" "public static $classname$ parseDelimitedFrom(java.io.InputStream input)\n" " throws java.io.IOException {\n" - " return parser().parseDelimitedFrom(input);\n" + " return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n" "}\n" "public static $classname$ parseDelimitedFrom(\n" " java.io.InputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return parser().parseDelimitedFrom(input, extensionRegistry);\n" + " return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.CodedInputStream input)\n" " throws java.io.IOException {\n" - " return parser().parseFrom(input);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, input);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.CodedInputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return parser().parseFrom(input, extensionRegistry);\n" + " return com.google.protobuf.GeneratedMessageLite.parseFrom(\n" + " DEFAULT_INSTANCE, input, extensionRegistry);\n" "}\n" "\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); @@ -789,11 +795,13 @@ void ImmutableMessageLiteGenerator::GenerateDynamicMethodIsInitialized( void ImmutableMessageLiteGenerator::GenerateDynamicMethodMakeImmutable( io::Printer* printer) { + // Output generation code for each field. for (int i = 0; i < descriptor_->field_count(); i++) { field_generators_.get(descriptor_->field(i)) .GenerateDynamicMethodMakeImmutableCode(printer); } + printer->Print( "return null;\n"); } @@ -808,19 +816,17 @@ void ImmutableMessageLiteGenerator::GenerateDynamicMethodNewBuilder( // =================================================================== -void ImmutableMessageLiteGenerator::GenerateDynamicMethodMergeFrom( +void ImmutableMessageLiteGenerator::GenerateDynamicMethodVisit( io::Printer* printer) { printer->Print( - // Optimization: If other is the default instance, we know none of its - // fields are set so we can skip the merge. - "if (arg0 == $classname$.getDefaultInstance()) return this;\n" - "$classname$ other = ($classname$) arg0;\n", + "Visitor visitor = (Visitor) arg0;\n" + "$classname$ other = ($classname$) arg1;\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); for (int i = 0; i < descriptor_->field_count(); i++) { if (!descriptor_->field(i)->containing_oneof()) { field_generators_.get( - descriptor_->field(i)).GenerateMergingCode(printer); + descriptor_->field(i)).GenerateVisitCode(printer); } } @@ -839,7 +845,7 @@ void ImmutableMessageLiteGenerator::GenerateDynamicMethodMergeFrom( "field_name", ToUpper(field->name())); printer->Indent(); - field_generators_.get(field).GenerateMergingCode(printer); + field_generators_.get(field).GenerateVisitCode(printer); printer->Print( "break;\n"); printer->Outdent(); @@ -848,26 +854,52 @@ void ImmutableMessageLiteGenerator::GenerateDynamicMethodMergeFrom( } printer->Print( "case $cap_oneof_name$_NOT_SET: {\n" + " visitor.visitOneofNotSet($oneof_name$Case_ != 0);\n" " break;\n" "}\n", "cap_oneof_name", ToUpper(context_->GetOneofGeneratorInfo( - descriptor_->oneof_decl(i))->name)); + descriptor_->oneof_decl(i))->name), + "oneof_name", + context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name); printer->Outdent(); printer->Print( "}\n"); } - // if message type has extensions - if (descriptor_->extension_range_count() > 0) { + printer->Print( + "if (visitor == com.google.protobuf.GeneratedMessageLite.MergeFromVisitor\n" + " .INSTANCE) {\n"); + printer->Indent(); + for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) { + const OneofDescriptor* field = descriptor_->oneof_decl(i); printer->Print( - "this.mergeExtensionFields(other);\n"); + "if (other.$oneof_name$Case_ != 0) {\n" + " $oneof_name$Case_ = other.$oneof_name$Case_;\n" + "}\n", + "oneof_name", context_->GetOneofGeneratorInfo(field)->name); } - if (PreserveUnknownFields(descriptor_)) { - printer->Print( - "this.mergeUnknownFields(other.unknownFields);\n"); + if (GenerateHasBits(descriptor_)) { + // Integers for bit fields. + int totalBits = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + totalBits += field_generators_.get(descriptor_->field(i)) + .GetNumBitsForMessage(); + } + int totalInts = (totalBits + 31) / 32; + + for (int i = 0; i < totalInts; i++) { + printer->Print( + "$bit_field_name$ |= other.$bit_field_name$;\n", + "bit_field_name", GetBitFieldName(i)); + } } + printer->Outdent(); + printer->Print( + "}\n"); + printer->Print( "return this;\n"); @@ -875,161 +907,13 @@ void ImmutableMessageLiteGenerator::GenerateDynamicMethodMergeFrom( // =================================================================== -namespace { -bool CheckHasBitsForEqualsAndHashCode(const FieldDescriptor* field) { - if (field->is_repeated()) { - return false; - } - if (SupportFieldPresence(field->file())) { - return true; - } - return GetJavaType(field) == JAVATYPE_MESSAGE && - field->containing_oneof() == NULL; -} -} // namespace - -void ImmutableMessageLiteGenerator:: -GenerateEqualsAndHashCode(io::Printer* printer) { - printer->Print( - "@java.lang.Override\n" - "public boolean equals(final java.lang.Object obj) {\n"); - printer->Indent(); - printer->Print( - "if (obj == this) {\n" - " return true;\n" - "}\n" - "if (!(obj instanceof $classname$)) {\n" - " return super.equals(obj);\n" - "}\n" - "$classname$ other = ($classname$) obj;\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - - printer->Print("boolean result = true;\n"); - for (int i = 0; i < descriptor_->field_count(); i++) { - const FieldDescriptor* field = descriptor_->field(i); - const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); - bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); - if (check_has_bits) { - printer->Print( - "result = result && (has$name$() == other.has$name$());\n" - "if (has$name$()) {\n", - "name", info->capitalized_name); - printer->Indent(); - } - field_generators_.get(field).GenerateEqualsCode(printer); - if (check_has_bits) { - printer->Outdent(); - printer->Print( - "}\n"); - } - } - if (PreserveUnknownFields(descriptor_)) { - // Always consider unknown fields for equality. This will sometimes return - // false for non-canonical ordering when running in LITE_RUNTIME but it's - // the best we can do. - printer->Print( - "result = result && unknownFields.equals(other.unknownFields);\n"); - } - printer->Print( - "return result;\n"); - printer->Outdent(); - printer->Print( - "}\n" - "\n"); - - printer->Print( - "@java.lang.Override\n" - "public int hashCode() {\n"); - printer->Indent(); - printer->Print( - "if (memoizedHashCode != 0) {\n"); - printer->Indent(); - printer->Print( - "return memoizedHashCode;\n"); - printer->Outdent(); - printer->Print( - "}\n" - "int hash = 41;\n"); - - // Include the hash of the class so that two objects with different types - // but the same field values will probably have different hashes. - printer->Print("hash = (19 * hash) + $classname$.class.hashCode();\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - - for (int i = 0; i < descriptor_->field_count(); i++) { - const FieldDescriptor* field = descriptor_->field(i); - const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); - bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); - if (check_has_bits) { - printer->Print( - "if (has$name$()) {\n", - "name", info->capitalized_name); - printer->Indent(); - } - field_generators_.get(field).GenerateHashCode(printer); - if (check_has_bits) { - printer->Outdent(); - printer->Print("}\n"); - } - } - - printer->Print( - "hash = (29 * hash) + unknownFields.hashCode();\n"); - printer->Print( - "memoizedHashCode = hash;\n" - "return hash;\n"); - printer->Outdent(); - printer->Print( - "}\n" - "\n"); -} - -// =================================================================== - -void ImmutableMessageLiteGenerator:: -GenerateExtensionRegistrationCode(io::Printer* printer) { - for (int i = 0; i < descriptor_->extension_count(); i++) { - ImmutableExtensionGenerator(descriptor_->extension(i), context_) - .GenerateRegistrationCode(printer); - } - - for (int i = 0; i < descriptor_->nested_type_count(); i++) { - ImmutableMessageLiteGenerator(descriptor_->nested_type(i), context_) - .GenerateExtensionRegistrationCode(printer); - } -} - -// =================================================================== -void ImmutableMessageLiteGenerator:: -GenerateParsingConstructor(io::Printer* printer) { - google::protobuf::scoped_array sorted_fields( - SortFieldsByNumber(descriptor_)); - - printer->Print( - "private $classname$(\n" - " com.google.protobuf.CodedInputStream input,\n" - " com.google.protobuf.ExtensionRegistryLite extensionRegistry) {\n", - "classname", descriptor_->name()); - printer->Indent(); - - // Initialize all fields to default. - GenerateInitializers(printer); - - // Use builder bits to track mutable repeated fields. - int totalBuilderBits = 0; - for (int i = 0; i < descriptor_->field_count(); i++) { - const ImmutableFieldLiteGenerator& field = - field_generators_.get(descriptor_->field(i)); - totalBuilderBits += field.GetNumBitsForBuilder(); - } - int totalBuilderInts = (totalBuilderBits + 31) / 32; - for (int i = 0; i < totalBuilderInts; i++) { - printer->Print("int mutable_$bit_field_name$ = 0;\n", - "bit_field_name", GetBitFieldName(i)); - } - +void ImmutableMessageLiteGenerator::GenerateDynamicMethodMergeFromStream( + io::Printer* printer) { printer->Print( + "com.google.protobuf.CodedInputStream input =\n" + " (com.google.protobuf.CodedInputStream) arg0;\n" + "com.google.protobuf.ExtensionRegistryLite extensionRegistry =\n" + " (com.google.protobuf.ExtensionRegistryLite) arg1;\n" "try {\n"); printer->Indent(); @@ -1077,6 +961,8 @@ GenerateParsingConstructor(io::Printer* printer) { "}\n"); } + google::protobuf::scoped_array sorted_fields( + SortFieldsByNumber(descriptor_)); for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = sorted_fields[i]; uint32 tag = WireFormatLite::MakeTag(field->number(), @@ -1130,19 +1016,54 @@ GenerateParsingConstructor(io::Printer* printer) { "} finally {\n"); printer->Indent(); - // Make repeated field list immutable. - for (int i = 0; i < descriptor_->field_count(); i++) { - const FieldDescriptor* field = sorted_fields[i]; - field_generators_.get(field).GenerateParsingDoneCode(printer); + printer->Outdent(); + printer->Print( + "}\n"); // finally +} + +// =================================================================== + +namespace { +bool CheckHasBitsForEqualsAndHashCode(const FieldDescriptor* field) { + if (field->is_repeated()) { + return false; + } + if (SupportFieldPresence(field->file())) { + return true; + } + return GetJavaType(field) == JAVATYPE_MESSAGE && + field->containing_oneof() == NULL; +} +} // namespace + +// =================================================================== + +void ImmutableMessageLiteGenerator:: +GenerateExtensionRegistrationCode(io::Printer* printer) { + for (int i = 0; i < descriptor_->extension_count(); i++) { + ImmutableExtensionLiteGenerator(descriptor_->extension(i), context_) + .GenerateRegistrationCode(printer); } + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + ImmutableMessageLiteGenerator(descriptor_->nested_type(i), context_) + .GenerateExtensionRegistrationCode(printer); + } +} + +// =================================================================== +void ImmutableMessageLiteGenerator:: +GenerateConstructor(io::Printer* printer) { printer->Print( - "doneParsing();\n"); + "private $classname$() {\n", + "classname", descriptor_->name()); + printer->Indent(); + + // Initialize all fields to default. + GenerateInitializers(printer); - printer->Outdent(); printer->Outdent(); printer->Print( - " }\n" // finally "}\n"); } @@ -1167,7 +1088,6 @@ void ImmutableMessageLiteGenerator::GenerateInitializers(io::Printer* printer) { } } - } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_message_lite.h b/src/google/protobuf/compiler/java/java_message_lite.h index 2bd3cdd46..292c1c56f 100644 --- a/src/google/protobuf/compiler/java/java_message_lite.h +++ b/src/google/protobuf/compiler/java/java_message_lite.h @@ -54,7 +54,8 @@ class ImmutableMessageLiteGenerator : public MessageGenerator { virtual void Generate(io::Printer* printer); virtual void GenerateInterface(io::Printer* printer); virtual void GenerateExtensionRegistrationCode(io::Printer* printer); - virtual void GenerateStaticVariables(io::Printer* printer); + virtual void GenerateStaticVariables( + io::Printer* printer, int* bytecode_estimate); virtual int GenerateStaticVariableInitializers(io::Printer* printer); private: @@ -69,12 +70,13 @@ class ImmutableMessageLiteGenerator : public MessageGenerator { void GenerateBuilder(io::Printer* printer); void GenerateDynamicMethodIsInitialized(io::Printer* printer); void GenerateDynamicMethodMakeImmutable(io::Printer* printer); - void GenerateDynamicMethodMergeFrom(io::Printer* printer); + void GenerateDynamicMethodVisit(io::Printer* printer); + void GenerateDynamicMethodMergeFromStream(io::Printer* printer); void GenerateDynamicMethodNewBuilder(io::Printer* printer); void GenerateInitializers(io::Printer* printer); void GenerateEqualsAndHashCode(io::Printer* printer); void GenerateParser(io::Printer* printer); - void GenerateParsingConstructor(io::Printer* printer); + void GenerateConstructor(io::Printer* printer); Context* context_; ClassNameResolver* name_resolver_; diff --git a/src/google/protobuf/compiler/java/java_name_resolver.cc b/src/google/protobuf/compiler/java/java_name_resolver.cc index 0c363f9fe..bffe4f16e 100644 --- a/src/google/protobuf/compiler/java/java_name_resolver.cc +++ b/src/google/protobuf/compiler/java/java_name_resolver.cc @@ -259,6 +259,13 @@ string ClassNameResolver::GetJavaImmutableClassName( descriptor->file(), true); } +string ClassNameResolver::GetJavaImmutableClassName( + const EnumDescriptor* descriptor) { + return GetJavaClassFullName( + ClassNameWithoutPackage(descriptor, true), + descriptor->file(), true); +} + } // namespace java } // namespace compiler diff --git a/src/google/protobuf/compiler/java/java_name_resolver.h b/src/google/protobuf/compiler/java/java_name_resolver.h index ab60b0a02..570d8d8f1 100644 --- a/src/google/protobuf/compiler/java/java_name_resolver.h +++ b/src/google/protobuf/compiler/java/java_name_resolver.h @@ -98,6 +98,7 @@ class ClassNameResolver { // For example: // com.package.OuterClass$OuterMessage$InnerMessage string GetJavaImmutableClassName(const Descriptor* descriptor); + string GetJavaImmutableClassName(const EnumDescriptor* descriptor); private: // Get the full name of a Java class by prepending the Java package name // or outer class name. diff --git a/src/google/protobuf/compiler/java/java_plugin_unittest.cc b/src/google/protobuf/compiler/java/java_plugin_unittest.cc index fe527623c..3e4910c88 100644 --- a/src/google/protobuf/compiler/java/java_plugin_unittest.cc +++ b/src/google/protobuf/compiler/java/java_plugin_unittest.cc @@ -44,9 +44,10 @@ #include #include +#include +#include #include #include -#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc index 178bbe190..e42ec2801 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -75,12 +75,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, "" : ("= " + ImmutableDefaultValue(descriptor, name_resolver)); (*variables)["capitalized_type"] = GetCapitalizedType(descriptor, /* immutable = */ true); - if (descriptor->is_packed()) { - (*variables)["tag"] = SimpleItoa(WireFormatLite::MakeTag( - descriptor->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED)); - } else { - (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); - } + (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); (*variables)["tag_size"] = SimpleItoa( WireFormat::TagSize(descriptor->number(), GetType(descriptor))); if (IsReferenceType(GetJavaType(descriptor))) { @@ -99,8 +94,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, if (fixed_size != -1) { (*variables)["fixed_size"] = SimpleItoa(fixed_size); } - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + (*variables)["on_changed"] = "onChanged();"; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -606,7 +600,7 @@ GenerateMembers(io::Printer* printer) const { "}\n"); if (descriptor_->is_packed() && - HasGeneratedMethods(descriptor_->containing_type())) { + context_->HasGeneratedMethods(descriptor_->containing_type())) { printer->Print(variables_, "private int $name$MemoizedSerializedSize = -1;\n"); } diff --git a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc index 5a7bf82df..690dad128 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc @@ -86,9 +86,6 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, case JAVATYPE_BOOLEAN: (*variables)["field_list_type"] = "com.google.protobuf.Internal." + capitalized_type + "List"; - (*variables)["new_list"] = "new" + capitalized_type + "List"; - (*variables)["new_list_with_capacity"] = - "new" + capitalized_type + "ListWithCapacity"; (*variables)["empty_list"] = "empty" + capitalized_type + "List()"; (*variables)["make_name_unmodifiable"] = (*variables)["name"] + "_.makeImmutable()"; @@ -98,19 +95,21 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, (*variables)["name"] + "_.add" + capitalized_type; (*variables)["repeated_set"] = (*variables)["name"] + "_.set" + capitalized_type; + (*variables)["visit_type"] = capitalized_type; + (*variables)["visit_type_list"] = "visit" + capitalized_type + "List"; break; default: (*variables)["field_list_type"] = "com.google.protobuf.Internal.ProtobufList<" + (*variables)["boxed_type"] + ">"; - (*variables)["new_list"] = "newProtobufList"; - (*variables)["new_list_with_capacity"] = "newProtobufListWithCapacity"; (*variables)["empty_list"] = "emptyProtobufList()"; (*variables)["make_name_unmodifiable"] = (*variables)["name"] + "_.makeImmutable()"; (*variables)["repeated_get"] = (*variables)["name"] + "_.get"; (*variables)["repeated_add"] = (*variables)["name"] + "_.add"; (*variables)["repeated_set"] = (*variables)["name"] + "_.set"; + (*variables)["visit_type"] = "ByteString"; + (*variables)["visit_type_list"] = "visitList"; } if (IsReferenceType(GetJavaType(descriptor))) { @@ -129,8 +128,6 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, if (fixed_size != -1) { (*variables)["fixed_size"] = SimpleItoa(fixed_size); } - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -299,17 +296,16 @@ GenerateBuilderClearCode(io::Printer* printer) const { } void ImmutablePrimitiveFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { +GenerateVisitCode(io::Printer* printer) const { if (SupportFieldPresence(descriptor_->file())) { printer->Print(variables_, - "if (other.has$capitalized_name$()) {\n" - " set$capitalized_name$(other.get$capitalized_name$());\n" - "}\n"); + "$name$_ = visitor.visit$visit_type$(\n" + " has$capitalized_name$(), $name$_,\n" + " other.has$capitalized_name$(), other.$name$_);\n"); } else { printer->Print(variables_, - "if (other.get$capitalized_name$() != $default$) {\n" - " set$capitalized_name$(other.get$capitalized_name$());\n" - "}\n"); + "$name$_ = visitor.visit$visit_type$($name$_ != $default$, $name$_,\n" + " other.$name$_ != $default$, other.$name$_);\n"); } } @@ -541,9 +537,10 @@ GenerateBuildingCode(io::Printer* printer) const { } void ImmutablePrimitiveOneofFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { +GenerateVisitCode(io::Printer* printer) const { printer->Print(variables_, - "set$capitalized_name$(other.get$capitalized_name$());\n"); + "$oneof_name$_ = visitor.visitOneof$visit_type$(\n" + " $has_oneof_case_message$, $oneof_name$_, other.$oneof_name$_);\n"); } void ImmutablePrimitiveOneofFieldLiteGenerator:: @@ -635,7 +632,7 @@ GenerateMembers(io::Printer* printer) const { "}\n"); if (descriptor_->options().packed() && - HasGeneratedMethods(descriptor_->containing_type())) { + context_->HasGeneratedMethods(descriptor_->containing_type())) { printer->Print(variables_, "private int $name$MemoizedSerializedSize = -1;\n"); } @@ -643,7 +640,8 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private void ensure$capitalized_name$IsMutable() {\n" " if (!$is_mutable$) {\n" - " $name$_ = $new_list$($name$_);\n" + " $name$_ =\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" " }\n" "}\n"); @@ -744,22 +742,9 @@ GenerateBuilderClearCode(io::Printer* printer) const { } void RepeatedImmutablePrimitiveFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { - // The code below does two optimizations: - // 1. If the other list is empty, there's nothing to do. This ensures we - // don't allocate a new array if we already have an immutable one. - // 2. If the other list is non-empty and our current list is empty, we can - // reuse the other list which is guaranteed to be immutable. +GenerateVisitCode(io::Printer* printer) const { printer->Print(variables_, - "if (!other.$name$_.isEmpty()) {\n" - " if ($name$_.isEmpty()) {\n" - " $name$_ = other.$name$_;\n" - " } else {\n" - " ensure$capitalized_name$IsMutable();\n" - " $name$_.addAll(other.$name$_);\n" - " }\n" - " $on_changed$\n" - "}\n"); + "$name$_= visitor.$visit_type_list$($name$_, other.$name$_);\n"); } void RepeatedImmutablePrimitiveFieldLiteGenerator:: @@ -780,7 +765,8 @@ GenerateParsingCode(io::Printer* printer) const { // TODO(dweis): Scan the input buffer to count and ensure capacity. printer->Print(variables_, "if (!$is_mutable$) {\n" - " $name$_ = $new_list$();\n" + " $name$_ =\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" "}\n" "$repeated_add$(input.read$capitalized_type$());\n"); } @@ -797,10 +783,13 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const { // TODO(dweis): Scan the input buffer to count, then initialize // appropriately. printer->Print(variables_, - " $name$_ = $new_list$();\n"); + " $name$_ =\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n"); } else { printer->Print(variables_, - " $name$_ = $new_list_with_capacity$(length/$fixed_size$);\n"); + " final int currentSize = $name$_.size();\n" + " $name$_ = $name$_.mutableCopyWithCapacity(\n" + " currentSize + (length/$fixed_size$));\n"); } // TODO(dweis): Scan the input buffer to count and ensure capacity. diff --git a/src/google/protobuf/compiler/java/java_primitive_field_lite.h b/src/google/protobuf/compiler/java/java_primitive_field_lite.h index ad603c2ac..6cfbbb98c 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field_lite.h +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.h @@ -69,7 +69,7 @@ class ImmutablePrimitiveFieldLiteGenerator void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; void GenerateBuilderClearCode(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; @@ -105,7 +105,7 @@ class ImmutablePrimitiveOneofFieldLiteGenerator void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; @@ -130,7 +130,7 @@ class RepeatedImmutablePrimitiveFieldLiteGenerator void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; void GenerateBuilderClearCode(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/java/java_shared_code_generator.cc b/src/google/protobuf/compiler/java/java_shared_code_generator.cc index 70177367a..74253c3fb 100644 --- a/src/google/protobuf/compiler/java/java_shared_code_generator.cc +++ b/src/google/protobuf/compiler/java/java_shared_code_generator.cc @@ -52,8 +52,9 @@ namespace compiler { namespace java { SharedCodeGenerator::SharedCodeGenerator(const FileDescriptor* file) - : name_resolver_(new ClassNameResolver), file_(file) { -} + : name_resolver_(new ClassNameResolver), + enforce_lite_(false), + file_(file) {} SharedCodeGenerator::~SharedCodeGenerator() { } @@ -63,7 +64,7 @@ void SharedCodeGenerator::Generate(GeneratorContext* context, string java_package = FileJavaPackage(file_); string package_dir = JavaPackageToDir(java_package); - if (HasDescriptorMethods(file_)) { + if (HasDescriptorMethods(file_, enforce_lite_)) { // Generate descriptors. string classname = name_resolver_->GetDescriptorClassName(file_); string filename = package_dir + classname + ".java"; diff --git a/src/google/protobuf/compiler/java/java_shared_code_generator.h b/src/google/protobuf/compiler/java/java_shared_code_generator.h index 38a32fc2d..3b573c075 100644 --- a/src/google/protobuf/compiler/java/java_shared_code_generator.h +++ b/src/google/protobuf/compiler/java/java_shared_code_generator.h @@ -72,6 +72,10 @@ class SharedCodeGenerator { void Generate(GeneratorContext* generator_context, vector* file_list); + void SetEnforceLite(bool value) { + enforce_lite_ = value; + } + void GenerateDescriptors(io::Printer* printer); private: @@ -81,6 +85,7 @@ class SharedCodeGenerator { bool ShouldIncludeDependency(const FileDescriptor* descriptor); google::protobuf::scoped_ptr name_resolver_; + bool enforce_lite_; const FileDescriptor* file_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SharedCodeGenerator); }; diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc index 7f757e476..b67eeb532 100644 --- a/src/google/protobuf/compiler/java/java_string_field.cc +++ b/src/google/protobuf/compiler/java/java_string_field.cc @@ -87,8 +87,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + (*variables)["on_changed"] = "onChanged();"; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -408,15 +407,6 @@ GenerateParsingCode(io::Printer* printer) const { "java.lang.String s = input.readStringRequireUtf8();\n" "$set_has_field_bit_message$\n" "$name$_ = s;\n"); - } else if (!HasDescriptorMethods(descriptor_->file())) { - // Lite runtime should attempt to reduce allocations by attempting to - // construct the string directly from the input stream buffer. This avoids - // spurious intermediary ByteString allocations, cutting overall allocations - // in half. - printer->Print(variables_, - "java.lang.String s = input.readString();\n" - "$set_has_field_bit_message$\n" - "$name$_ = s;\n"); } else { printer->Print(variables_, "com.google.protobuf.ByteString bs = input.readBytes();\n" @@ -668,15 +658,6 @@ GenerateParsingCode(io::Printer* printer) const { "java.lang.String s = input.readStringRequireUtf8();\n" "$set_oneof_case_message$;\n" "$oneof_name$_ = s;\n"); - } else if (!HasDescriptorMethods(descriptor_->file())) { - // Lite runtime should attempt to reduce allocations by attempting to - // construct the string directly from the input stream buffer. This avoids - // spurious intermediary ByteString allocations, cutting overall allocations - // in half. - printer->Print(variables_, - "java.lang.String s = input.readString();\n" - "$set_oneof_case_message$;\n" - "$oneof_name$_ = s;\n"); } else { printer->Print(variables_, "com.google.protobuf.ByteString bs = input.readBytes();\n" @@ -934,13 +915,6 @@ GenerateParsingCode(io::Printer* printer) const { if (CheckUtf8(descriptor_)) { printer->Print(variables_, "java.lang.String s = input.readStringRequireUtf8();\n"); - } else if (!HasDescriptorMethods(descriptor_->file())) { - // Lite runtime should attempt to reduce allocations by attempting to - // construct the string directly from the input stream buffer. This avoids - // spurious intermediary ByteString allocations, cutting overall allocations - // in half. - printer->Print(variables_, - "java.lang.String s = input.readString();\n"); } else { printer->Print(variables_, "com.google.protobuf.ByteString bs = input.readBytes();\n"); @@ -950,7 +924,7 @@ GenerateParsingCode(io::Printer* printer) const { " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" " $set_mutable_bit_parser$;\n" "}\n"); - if (CheckUtf8(descriptor_) || !HasDescriptorMethods(descriptor_->file())) { + if (CheckUtf8(descriptor_)) { printer->Print(variables_, "$name$_.add(s);\n"); } else { diff --git a/src/google/protobuf/compiler/java/java_string_field_lite.cc b/src/google/protobuf/compiler/java/java_string_field_lite.cc index eb5964bd3..0b92c0218 100644 --- a/src/google/protobuf/compiler/java/java_string_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_string_field_lite.cc @@ -84,8 +84,6 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = - HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; if (SupportFieldPresence(descriptor->file())) { // For singular messages and builders, one bit is used for the hasField bit. @@ -103,7 +101,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, (*variables)["clear_has_field_bit_message"] = ""; (*variables)["is_field_present_message"] = - "!get" + (*variables)["capitalized_name"] + ".isEmpty()"; + "!" + (*variables)["name"] + "_.isEmpty()"; } // For repeated builders, the underlying list tracks mutability state. @@ -301,22 +299,16 @@ GenerateInitializationCode(io::Printer* printer) const { } void ImmutableStringFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { +GenerateVisitCode(io::Printer* printer) const { if (SupportFieldPresence(descriptor_->file())) { - // Allow a slight breach of abstraction here in order to avoid forcing - // all string fields to Strings when copying fields from a Message. printer->Print(variables_, - "if (other.has$capitalized_name$()) {\n" - " $set_has_field_bit_message$\n" - " $name$_ = other.$name$_;\n" - " $on_changed$\n" - "}\n"); + "$name$_ = visitor.visitString(\n" + " has$capitalized_name$(), $name$_,\n" + " other.has$capitalized_name$(), other.$name$_);\n"); } else { printer->Print(variables_, - "if (!other.get$capitalized_name$().isEmpty()) {\n" - " $name$_ = other.$name$_;\n" - " $on_changed$\n" - "}\n"); + "$name$_ = visitor.visitString(!$name$_.isEmpty(), $name$_,\n" + " !other.$name$_.isEmpty(), other.$name$_);\n"); } } @@ -523,13 +515,10 @@ GenerateBuilderMembers(io::Printer* printer) const { } void ImmutableStringOneofFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { - // Allow a slight breach of abstraction here in order to avoid forcing - // all string fields to Strings when copying fields from a Message. +GenerateVisitCode(io::Printer* printer) const { printer->Print(variables_, - "$set_oneof_case_message$;\n" - "$oneof_name$_ = other.$oneof_name$_;\n" - "$on_changed$\n"); + "$oneof_name$_ = visitor.visitOneofString(\n" + " $has_oneof_case_message$, $oneof_name$_, other.$oneof_name$_);\n"); } void ImmutableStringOneofFieldLiteGenerator:: @@ -650,8 +639,8 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private void ensure$capitalized_name$IsMutable() {\n" " if (!$is_mutable$) {\n" - " $name$_ = com.google.protobuf.GeneratedMessageLite.newProtobufList(\n" - " $name$_);\n" + " $name$_ =\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" " }\n" "}\n"); @@ -778,22 +767,9 @@ GenerateInitializationCode(io::Printer* printer) const { } void RepeatedImmutableStringFieldLiteGenerator:: -GenerateMergingCode(io::Printer* printer) const { - // The code below does two optimizations: - // 1. If the other list is empty, there's nothing to do. This ensures we - // don't allocate a new array if we already have an immutable one. - // 2. If the other list is non-empty and our current list is empty, we can - // reuse the other list which is guaranteed to be immutable. +GenerateVisitCode(io::Printer* printer) const { printer->Print(variables_, - "if (!other.$name$_.isEmpty()) {\n" - " if ($name$_.isEmpty()) {\n" - " $name$_ = other.$name$_;\n" - " } else {\n" - " ensure$capitalized_name$IsMutable();\n" - " $name$_.addAll(other.$name$_);\n" - " }\n" - " $on_changed$\n" - "}\n"); + "$name$_= visitor.visitList($name$_, other.$name$_);\n"); } void RepeatedImmutableStringFieldLiteGenerator:: @@ -817,15 +793,11 @@ GenerateParsingCode(io::Printer* printer) const { } printer->Print(variables_, "if (!$is_mutable$) {\n" - " $name$_ = com.google.protobuf.GeneratedMessageLite.newProtobufList();\n" + " $name$_ =\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" "}\n"); - if (CheckUtf8(descriptor_) || !HasDescriptorMethods(descriptor_->file())) { - printer->Print(variables_, - "$name$_.add(s);\n"); - } else { - printer->Print(variables_, - "$name$_.add(bs);\n"); - } + printer->Print(variables_, + "$name$_.add(s);\n"); } void RepeatedImmutableStringFieldLiteGenerator:: diff --git a/src/google/protobuf/compiler/java/java_string_field_lite.h b/src/google/protobuf/compiler/java/java_string_field_lite.h index 4d9b4bd7c..4148aa4df 100644 --- a/src/google/protobuf/compiler/java/java_string_field_lite.h +++ b/src/google/protobuf/compiler/java/java_string_field_lite.h @@ -68,7 +68,7 @@ class ImmutableStringFieldLiteGenerator : public ImmutableFieldLiteGenerator { void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateParsingDoneCode(io::Printer* printer) const; @@ -103,7 +103,7 @@ class ImmutableStringOneofFieldLiteGenerator private: void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; @@ -126,7 +126,7 @@ class RepeatedImmutableStringFieldLiteGenerator void GenerateMembers(io::Printer* printer) const; void GenerateBuilderMembers(io::Printer* printer) const; void GenerateInitializationCode(io::Printer* printer) const; - void GenerateMergingCode(io::Printer* printer) const; + void GenerateVisitCode(io::Printer* printer) const; void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; void GenerateParsingDoneCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/javanano/javanano_generator.cc b/src/google/protobuf/compiler/javanano/javanano_generator.cc index a33eba1b0..7c3a04212 100644 --- a/src/google/protobuf/compiler/javanano/javanano_generator.cc +++ b/src/google/protobuf/compiler/javanano/javanano_generator.cc @@ -68,12 +68,10 @@ void UpdateParamsRecursively(Params& params, } if (file->options().has_java_package()) { string result = file->options().java_package(); - if (!file->options().javanano_use_deprecated_package()) { - if (!result.empty()) { - result += "."; - } - result += "nano"; + if (!result.empty()) { + result += "."; } + result += "nano"; params.set_java_package( file->name(), result); } diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.cc b/src/google/protobuf/compiler/javanano/javanano_helpers.cc index 5465655fb..02811a24d 100644 --- a/src/google/protobuf/compiler/javanano/javanano_helpers.cc +++ b/src/google/protobuf/compiler/javanano/javanano_helpers.cc @@ -201,12 +201,10 @@ string FileJavaPackage(const Params& params, const FileDescriptor* file) { result += file->package(); } - if (!file->options().javanano_use_deprecated_package()) { - if (!result.empty()) { - result += "."; - } - result += "nano"; + if (!result.empty()) { + result += "."; } + result += "nano"; return result; } diff --git a/src/google/protobuf/compiler/js/js_generator.cc b/src/google/protobuf/compiler/js/js_generator.cc index 0de7e2c6f..3de61e80b 100755 --- a/src/google/protobuf/compiler/js/js_generator.cc +++ b/src/google/protobuf/compiler/js/js_generator.cc @@ -28,7 +28,7 @@ // (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 +#include "google/protobuf/compiler/js/js_generator.h" #include #include @@ -123,6 +123,16 @@ static const int kNumKeyword = sizeof(kKeyword) / sizeof(char*); namespace { +// The mode of operation for bytes fields. Historically JSPB always carried +// bytes as JS {string}, containing base64 content by convention. With binary +// and proto3 serialization the new convention is to represent it as binary +// data in Uint8Array. See b/26173701 for background on the migration. +enum BytesMode { + BYTES_DEFAULT, // Default type for getBytesField to return. + BYTES_B64, // Explicitly coerce to base64 string where needed. + BYTES_U8, // Explicitly coerce to Uint8Array where needed. +}; + bool IsReserved(const string& ident) { for (int i = 0; i < kNumKeyword; i++) { if (ident == kKeyword[i]) { @@ -134,7 +144,7 @@ bool IsReserved(const string& ident) { // Returns a copy of |filename| with any trailing ".protodevel" or ".proto // suffix stripped. -// TODO(robinson): Unify with copy in compiler/cpp/internal/helpers.cc. +// TODO(haberman): Unify with copy in compiler/cpp/internal/helpers.cc. string StripProto(const string& filename) { const char* suffix = HasSuffixString(filename, ".protodevel") ? ".protodevel" : ".proto"; @@ -144,9 +154,7 @@ string StripProto(const string& filename) { // Given a filename like foo/bar/baz.proto, returns the correspoding JavaScript // file foo/bar/baz.js. string GetJSFilename(const string& filename) { - const char* suffix = HasSuffixString(filename, ".protodevel") - ? ".protodevel" : ".proto"; - return StripSuffixString(filename, suffix) + "_pb.js"; + return StripProto(filename) + "_pb.js"; } // Given a filename like foo/bar/baz.proto, returns the root directory @@ -385,15 +393,47 @@ string ToFileName(const string& input) { return result; } +// When we're generating one output file per type name, this is the filename +// that top-level extensions should go in. +string GetExtensionFileName(const GeneratorOptions& options, + const FileDescriptor* file) { + return options.output_dir + "/" + ToFileName(GetPath(options, file)) + ".js"; +} + +// When we're generating one output file per type name, this is the filename +// that a top-level message should go in. +string GetMessageFileName(const GeneratorOptions& options, + const Descriptor* desc) { + return options.output_dir + "/" + ToFileName(desc->name()) + ".js"; +} + +// When we're generating one output file per type name, this is the filename +// that a top-level message should go in. +string GetEnumFileName(const GeneratorOptions& options, + const EnumDescriptor* desc) { + return options.output_dir + "/" + ToFileName(desc->name()) + ".js"; +} + // Returns the message/response ID, if set. string GetMessageId(const Descriptor* desc) { return string(); } +bool IgnoreExtensionField(const FieldDescriptor* field) { + // Exclude descriptor extensions from output "to avoid clutter" (from original + // codegen). + return field->is_extension() && + field->containing_type()->file()->name() == + "google/protobuf/descriptor.proto"; +} + // Used inside Google only -- do not remove. bool IsResponse(const Descriptor* desc) { return false; } -bool IgnoreField(const FieldDescriptor* field) { return false; } + +bool IgnoreField(const FieldDescriptor* field) { + return IgnoreExtensionField(field); +} // Does JSPB ignore this entire oneof? True only if all fields are ignored. @@ -438,12 +478,32 @@ string JSObjectFieldName(const FieldDescriptor* field) { return name; } +string JSByteGetterSuffix(BytesMode bytes_mode) { + switch (bytes_mode) { + case BYTES_DEFAULT: + return ""; + case BYTES_B64: + return "B64"; + case BYTES_U8: + return "U8"; + default: + assert(false); + } +} + // Returns the field name as a capitalized portion of a getter/setter method // name, e.g. MyField for .getMyField(). -string JSGetterName(const FieldDescriptor* field) { +string JSGetterName(const FieldDescriptor* field, + BytesMode bytes_mode = BYTES_DEFAULT) { string name = JSIdent(field, /* is_upper_camel = */ true, /* is_map = */ false); + if (field->type() == FieldDescriptor::TYPE_BYTES) { + string suffix = JSByteGetterSuffix(bytes_mode); + if (!suffix.empty()) { + name += "_as" + suffix; + } + } if (name == "Extension" || name == "JsPbMessageId") { // Avoid conflicts with base-class names. name += "$"; @@ -504,8 +564,7 @@ string JSOneofIndex(const OneofDescriptor* oneof) { return SimpleItoa(index); } -// Decodes a codepoint in \x0000 -- \xFFFF. Since JS strings are UTF-16, we only -// need to handle the BMP (16-bit range) here. +// Decodes a codepoint in \x0000 -- \xFFFF. uint16 DecodeUTF8Codepoint(uint8* bytes, size_t* length) { if (*length == 0) { return 0; @@ -542,80 +601,56 @@ uint16 DecodeUTF8Codepoint(uint8* bytes, size_t* length) { } // Escapes the contents of a string to be included within double-quotes ("") in -// JavaScript. |is_utf8| determines whether the input data (in a C++ string of -// chars) is UTF-8 encoded (in which case codepoints become JavaScript string -// characters, escaped with 16-bit hex escapes where necessary) or raw binary -// (in which case bytes become JavaScript string characters 0 -- 255). -string EscapeJSString(const string& in, bool is_utf8) { - string result; +// JavaScript. The input data should be a UTF-8 encoded C++ string of chars. +// Returns false if |out| was truncated because |in| contained invalid UTF-8 or +// codepoints outside the BMP. +// TODO(lukestebbing): Support codepoints outside the BMP. +bool EscapeJSString(const string& in, string* out) { size_t decoded = 0; for (size_t i = 0; i < in.size(); i += decoded) { uint16 codepoint = 0; - if (is_utf8) { - // Decode the next UTF-8 codepoint. - size_t have_bytes = in.size() - i; - uint8 bytes[3] = { + // Decode the next UTF-8 codepoint. + size_t have_bytes = in.size() - i; + uint8 bytes[3] = { static_cast(in[i]), static_cast(((i + 1) < in.size()) ? in[i + 1] : 0), static_cast(((i + 2) < in.size()) ? in[i + 2] : 0), - }; - codepoint = DecodeUTF8Codepoint(bytes, &have_bytes); - if (have_bytes == 0) { - break; - } - decoded = have_bytes; - } else { - codepoint = static_cast(static_cast(in[i])); - decoded = 1; + }; + codepoint = DecodeUTF8Codepoint(bytes, &have_bytes); + if (have_bytes == 0) { + return false; } - - // Next byte -- used for minimal octal escapes below. - char next_byte = (i + decoded) < in.size() ? - in[i + decoded] : 0; - bool pad_octal = (next_byte >= '0' && next_byte <= '7'); + decoded = have_bytes; switch (codepoint) { - case '\0': result += pad_octal ? "\\000" : "\\0"; break; - case '\b': result += "\\\b"; break; - case '\t': result += "\\\t"; break; - case '\n': result += "\\\n"; break; - case '\r': result += "\\\r"; break; - case '\f': result += "\\\f"; break; - case '\\': result += "\\\\"; break; - case '"': result += pad_octal ? "\\042" : "\\42"; break; - case '&': result += pad_octal ? "\\046" : "\\46"; break; - case '\'': result += pad_octal ? "\\047" : "\\47"; break; - case '<': result += pad_octal ? "\\074" : "\\74"; break; - case '=': result += pad_octal ? "\\075" : "\\75"; break; - case '>': result += pad_octal ? "\\076" : "\\76"; break; + case '\'': *out += "\\x27"; break; + case '"': *out += "\\x22"; break; + case '<': *out += "\\x3c"; break; + case '=': *out += "\\x3d"; break; + case '>': *out += "\\x3e"; break; + case '&': *out += "\\x26"; break; + case '\b': *out += "\\b"; break; + case '\t': *out += "\\t"; break; + case '\n': *out += "\\n"; break; + case '\f': *out += "\\f"; break; + case '\r': *out += "\\r"; break; + case '\\': *out += "\\\\"; break; default: - // All other non-ASCII codepoints are escaped. - // Original codegen uses hex for >= 0x100 and octal for others. + // TODO(lukestebbing): Once we're supporting codepoints outside the BMP, + // use a single Unicode codepoint escape if the output language is + // ECMAScript 2015 or above. Otherwise, use a surrogate pair. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#String_literals if (codepoint >= 0x20 && codepoint <= 0x7e) { - result += static_cast(codepoint); + *out += static_cast(codepoint); + } else if (codepoint >= 0x100) { + *out += StringPrintf("\\u%04x", codepoint); } else { - if (codepoint >= 0x100) { - result += StringPrintf("\\u%04x", codepoint); - } else { - if (pad_octal || codepoint >= 0100) { - result += "\\"; - result += ('0' + ((codepoint >> 6) & 07)); - result += ('0' + ((codepoint >> 3) & 07)); - result += ('0' + ((codepoint >> 0) & 07)); - } else if (codepoint >= 010) { - result += "\\"; - result += ('0' + ((codepoint >> 3) & 07)); - result += ('0' + ((codepoint >> 0) & 07)); - } else { - result += "\\"; - result += ('0' + ((codepoint >> 0) & 07)); - } - } + *out += StringPrintf("\\x%02x", codepoint); } break; } } - return result; + return true; } string EscapeBase64(const string& in) { @@ -740,11 +775,17 @@ string JSFieldDefault(const FieldDescriptor* field) { return DoubleToString(field->default_value_double()); case FieldDescriptor::CPPTYPE_STRING: if (field->type() == FieldDescriptor::TYPE_STRING) { - return "\"" + EscapeJSString(field->default_value_string(), true) + - "\""; - } else { - return "\"" + EscapeBase64(field->default_value_string()) + - "\""; + string out; + bool is_valid = EscapeJSString(field->default_value_string(), &out); + if (!is_valid) { + // TODO(lukestebbing): Decide whether this should be a hard error. + GOOGLE_LOG(WARNING) << "The default value for field " << field->full_name() + << " was truncated since it contained invalid UTF-8 or" + " codepoints outside the basic multilingual plane."; + } + return "\"" + out + "\""; + } else { // Bytes + return "\"" + EscapeBase64(field->default_value_string()) + "\""; } case FieldDescriptor::CPPTYPE_MESSAGE: return "null"; @@ -801,8 +842,27 @@ string JSIntegerTypeName(const FieldDescriptor* field) { return "number"; } +string JSStringTypeName(const GeneratorOptions& options, + const FieldDescriptor* field, + BytesMode bytes_mode) { + if (field->type() == FieldDescriptor::TYPE_BYTES) { + switch (bytes_mode) { + case BYTES_DEFAULT: + return "(string|Uint8Array)"; + case BYTES_B64: + return "string"; + case BYTES_U8: + return "Uint8Array"; + default: + assert(false); + } + } + return "string"; +} + string JSTypeName(const GeneratorOptions& options, - const FieldDescriptor* field) { + const FieldDescriptor* field, + BytesMode bytes_mode) { switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_BOOL: return "boolean"; @@ -819,7 +879,7 @@ string JSTypeName(const GeneratorOptions& options, case FieldDescriptor::CPPTYPE_DOUBLE: return "number"; case FieldDescriptor::CPPTYPE_STRING: - return "string"; + return JSStringTypeName(options, field, bytes_mode); case FieldDescriptor::CPPTYPE_ENUM: return GetPath(options, field->enum_type()); case FieldDescriptor::CPPTYPE_MESSAGE: @@ -836,20 +896,26 @@ string JSFieldTypeAnnotation(const GeneratorOptions& options, bool force_optional, bool force_present, bool singular_if_not_packed, - bool always_singular) { + BytesMode bytes_mode = BYTES_DEFAULT) { bool is_primitive = (field->cpp_type() != FieldDescriptor::CPPTYPE_ENUM && - field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE); + field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE && + (field->type() != FieldDescriptor::TYPE_BYTES || + bytes_mode == BYTES_B64)); - string jstype = JSTypeName(options, field); + string jstype = JSTypeName(options, field, bytes_mode); if (field->is_repeated() && - !always_singular && (field->is_packed() || !singular_if_not_packed)) { - if (!is_primitive) { - jstype = "!" + jstype; + if (field->type() == FieldDescriptor::TYPE_BYTES && + bytes_mode == BYTES_DEFAULT) { + jstype = "(Array|Array)"; + } else { + if (!is_primitive) { + jstype = "!" + jstype; + } + jstype = "Array.<" + jstype + ">"; } - jstype = "Array.<" + jstype + ">"; if (!force_optional) { jstype = "!" + jstype; } @@ -884,10 +950,6 @@ string JSBinaryReaderMethodType(const FieldDescriptor* field) { string JSBinaryReadWriteMethodName(const FieldDescriptor* field, bool is_writer) { string name = JSBinaryReaderMethodType(field); - if (is_writer && field->type() == FieldDescriptor::TYPE_BYTES) { - // Override for `bytes` fields: treat string as raw bytes, not base64. - name = "BytesRawString"; - } if (field->is_packed()) { name = "Packed" + name; } else if (is_writer && field->is_repeated()) { @@ -1044,7 +1106,7 @@ string FieldDefinition(const GeneratorOptions& options, field->number()); } -string FieldComments(const FieldDescriptor* field) { +string FieldComments(const FieldDescriptor* field, BytesMode bytes_mode) { string comments; if (field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL) { comments += @@ -1060,6 +1122,11 @@ string FieldComments(const FieldDescriptor* field) { " * replace the array itself, then you must call the setter to " "update it.\n"; } + if (field->type() == FieldDescriptor::TYPE_BYTES && bytes_mode == BYTES_U8) { + comments += + " * Note that Uint8Array is not supported on all browsers.\n" + " * @see http://caniuse.com/Uint8Array\n"; + } return comments; } @@ -1070,8 +1137,10 @@ bool ShouldGenerateExtension(const FieldDescriptor* field) { } bool HasExtensions(const Descriptor* desc) { - if (desc->extension_count() > 0) { - return true; + for (int i = 0; i < desc->extension_count(); i++) { + if (ShouldGenerateExtension(desc->extension(i))) { + return true; + } } for (int i = 0; i < desc->nested_type_count(); i++) { if (HasExtensions(desc->nested_type(i))) { @@ -1123,7 +1192,7 @@ string GetPivot(const Descriptor* desc) { } // Returns true for fields that represent "null" as distinct from the default -// value. See https://go/proto3#heading=h.kozewqqcqhuz for more information. +// value. See http://go/proto3#heading=h.kozewqqcqhuz for more information. bool HasFieldPresence(const FieldDescriptor* field) { return (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) || @@ -1132,7 +1201,7 @@ bool HasFieldPresence(const FieldDescriptor* field) { } // For proto3 fields without presence, returns a string representing the default -// value in JavaScript. See https://go/proto3#heading=h.kozewqqcqhuz for more +// value in JavaScript. See http://go/proto3#heading=h.kozewqqcqhuz for more // information. string Proto3PrimitiveFieldDefault(const FieldDescriptor* field) { switch (field->cpp_type()) { @@ -1151,16 +1220,153 @@ string Proto3PrimitiveFieldDefault(const FieldDescriptor* field) { case FieldDescriptor::CPPTYPE_BOOL: return "false"; - case FieldDescriptor::CPPTYPE_STRING: + case FieldDescriptor::CPPTYPE_STRING: // includes BYTES return "\"\""; default: - // BYTES and MESSAGE are handled separately. + // MESSAGE is handled separately. assert(false); return ""; } } +// We use this to implement the semantics that same file can be generated +// multiple times, but the last one wins. We never actually write the files, +// but we keep a set of which descriptors were the final one for a given +// filename. +class FileDeduplicator { + public: + explicit FileDeduplicator(const GeneratorOptions& options) + : error_on_conflict_(options.error_on_name_conflict) {} + + bool AddFile(const string& filename, const void* desc, string* error) { + if (descs_by_filename_.find(filename) != descs_by_filename_.end()) { + if (error_on_conflict_) { + *error = "Name conflict: file name " + filename + + " would be generated by two descriptors"; + return false; + } + allowed_descs_.erase(descs_by_filename_[filename]); + } + + descs_by_filename_[filename] = desc; + allowed_descs_.insert(desc); + return true; + } + + void GetAllowedSet(set* allowed_set) { + *allowed_set = allowed_descs_; + } + + private: + bool error_on_conflict_; + map descs_by_filename_; + set allowed_descs_; +}; + +void DepthFirstSearch(const FileDescriptor* file, + vector* list, + set* seen) { + if (!seen->insert(file).second) { + return; + } + + // Add all dependencies. + for (int i = 0; i < file->dependency_count(); i++) { + DepthFirstSearch(file->dependency(i), list, seen); + } + + // Add this file. + list->push_back(file); +} + +// A functor for the predicate to remove_if() below. Returns true if a given +// FileDescriptor is not in the given set. +class NotInSet { + public: + explicit NotInSet(const set& file_set) + : file_set_(file_set) {} + + bool operator()(const FileDescriptor* file) { + return file_set_.count(file) == 0; + } + + private: + const set& file_set_; +}; + +// This function generates an ordering of the input FileDescriptors that matches +// the logic of the old code generator. The order is significant because two +// different input files can generate the same output file, and the last one +// needs to win. +void GenerateJspbFileOrder(const vector& input, + vector* ordered) { + // First generate an ordering of all reachable files (including dependencies) + // with depth-first search. This mimics the behavior of --include_imports, + // which is what the old codegen used. + ordered->clear(); + set seen; + set input_set; + for (int i = 0; i < input.size(); i++) { + DepthFirstSearch(input[i], ordered, &seen); + input_set.insert(input[i]); + } + + // Now remove the entries that are not actually in our input list. + ordered->erase( + std::remove_if(ordered->begin(), ordered->end(), NotInSet(input_set)), + ordered->end()); +} + +// If we're generating code in file-per-type mode, avoid overwriting files +// by choosing the last descriptor that writes each filename and permitting +// only those to generate code. + +bool GenerateJspbAllowedSet(const GeneratorOptions& options, + const vector& files, + set* allowed_set, + string* error) { + vector files_ordered; + GenerateJspbFileOrder(files, &files_ordered); + + // Choose the last descriptor for each filename. + FileDeduplicator dedup(options); + for (int i = 0; i < files_ordered.size(); i++) { + for (int j = 0; j < files_ordered[i]->message_type_count(); j++) { + const Descriptor* desc = files_ordered[i]->message_type(j); + if (!dedup.AddFile(GetMessageFileName(options, desc), desc, error)) { + return false; + } + } + for (int j = 0; j < files_ordered[i]->enum_type_count(); j++) { + const EnumDescriptor* desc = files_ordered[i]->enum_type(j); + if (!dedup.AddFile(GetEnumFileName(options, desc), desc, error)) { + return false; + } + } + + // Pull out all free-floating extensions and generate files for those too. + bool has_extension = false; + + for (int j = 0; j < files_ordered[i]->extension_count(); j++) { + if (ShouldGenerateExtension(files_ordered[i]->extension(j))) { + has_extension = true; + } + } + + if (has_extension) { + if (!dedup.AddFile(GetExtensionFileName(options, files_ordered[i]), + files_ordered[i], error)) { + return false; + } + } + } + + dedup.GetAllowedSet(allowed_set); + + return true; +} + } // anonymous namespace void Generator::GenerateHeader(const GeneratorOptions& options, @@ -1251,10 +1457,10 @@ void Generator::GenerateProvides(const GeneratorOptions& options, } } -void Generator::GenerateRequires(const GeneratorOptions& options, - io::Printer* printer, - const Descriptor* desc, - std::set* provided) const { +void Generator::GenerateRequiresForMessage(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc, + std::set* provided) const { std::set required; std::set forwards; bool have_message = false; @@ -1266,55 +1472,50 @@ void Generator::GenerateRequires(const GeneratorOptions& options, /* require_extension = */ HasExtensions(desc)); } -void Generator::GenerateRequires(const GeneratorOptions& options, - io::Printer* printer, - const vector& files, - std::set* provided) const { - if (options.import_style == GeneratorOptions::IMPORT_BROWSER) { - return; - } else if (options.import_style == GeneratorOptions::IMPORT_CLOSURE) { - // For Closure imports we need to import every message type individually. - std::set required; - std::set forwards; - bool have_extensions = false; - bool have_message = false; +void Generator::GenerateRequiresForLibrary( + const GeneratorOptions& options, io::Printer* printer, + const vector& files, + std::set* provided) const { + GOOGLE_CHECK_EQ(options.import_style, GeneratorOptions::IMPORT_CLOSURE); + // For Closure imports we need to import every message type individually. + std::set required; + std::set forwards; + bool have_extensions = false; + bool have_message = false; - for (int i = 0; i < files.size(); i++) { - for (int j = 0; j < files[i]->message_type_count(); j++) { - FindRequiresForMessage(options, - files[i]->message_type(j), - &required, &forwards, &have_message); - } - if (!have_extensions && HasExtensions(files[i])) { - have_extensions = true; - } - - for (int j = 0; j < files[i]->extension_count(); j++) { - const FieldDescriptor* extension = files[i]->extension(j); - if (IgnoreField(extension)) { - continue; - } - if (extension->containing_type()->full_name() != - "google.protobuf.bridge.MessageSet") { - required.insert(GetPath(options, extension->containing_type())); - } - FindRequiresForField(options, extension, &required, &forwards); - have_extensions = true; - } + for (int i = 0; i < files.size(); i++) { + for (int j = 0; j < files[i]->message_type_count(); j++) { + FindRequiresForMessage(options, + files[i]->message_type(j), + &required, &forwards, &have_message); + } + if (!have_extensions && HasExtensions(files[i])) { + have_extensions = true; } - GenerateRequiresImpl(options, printer, &required, &forwards, provided, - /* require_jspb = */ have_message, - /* require_extension = */ have_extensions); - } else if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) { - // CommonJS imports are based on files + for (int j = 0; j < files[i]->extension_count(); j++) { + const FieldDescriptor* extension = files[i]->extension(j); + if (IgnoreField(extension)) { + continue; + } + if (extension->containing_type()->full_name() != + "google.protobuf.bridge.MessageSet") { + required.insert(GetPath(options, extension->containing_type())); + } + FindRequiresForField(options, extension, &required, &forwards); + have_extensions = true; + } } + + GenerateRequiresImpl(options, printer, &required, &forwards, provided, + /* require_jspb = */ have_message, + /* require_extension = */ have_extensions); } -void Generator::GenerateRequires(const GeneratorOptions& options, - io::Printer* printer, - const vector& fields, - std::set* provided) const { +void Generator::GenerateRequiresForExtensions( + const GeneratorOptions& options, io::Printer* printer, + const vector& fields, + std::set* provided) const { std::set required; std::set forwards; for (int i = 0; i < fields.size(); i++) { @@ -1708,7 +1909,7 @@ void Generator::GenerateClassToObject(const GeneratorOptions& options, printer->Print( " if (includeInstance) {\n" - " obj.$$jspbMessageInstance = msg\n" + " obj.$$jspbMessageInstance = msg;\n" " }\n" " return obj;\n" "};\n" @@ -1741,17 +1942,34 @@ void Generator::GenerateClassFieldToObject(const GeneratorOptions& options, } } else { // Simple field (singular or repeated). - if (!HasFieldPresence(field) && !field->is_repeated()) { + if ((!HasFieldPresence(field) && !field->is_repeated()) || + field->type() == FieldDescriptor::TYPE_BYTES) { // Delegate to the generated get() method in order not to duplicate - // the proto3-field-default-value logic here. + // the proto3-field-default-value or byte-coercion logic here. printer->Print("msg.get$getter$()", - "getter", JSGetterName(field)); + "getter", JSGetterName(field, BYTES_B64)); } else { if (field->has_default_value()) { - printer->Print("jspb.Message.getField(msg, $index$) != null ? " - "jspb.Message.getField(msg, $index$) : $defaultValue$", + printer->Print("jspb.Message.getField(msg, $index$) == null ? " + "$defaultValue$ : ", "index", JSFieldIndex(field), "defaultValue", JSFieldDefault(field)); + } + if (field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT || + field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE) { + if (field->is_repeated()) { + printer->Print("jspb.Message.getRepeatedFloatingPointField(" + "msg, $index$)", + "index", JSFieldIndex(field)); + } else if (field->is_optional() && !field->has_default_value()) { + printer->Print("jspb.Message.getOptionalFloatingPointField(" + "msg, $index$)", + "index", JSFieldIndex(field)); + } else { + // Convert "NaN" to NaN. + printer->Print("+jspb.Message.getField(msg, $index$)", + "index", JSFieldIndex(field)); + } } else { printer->Print("jspb.Message.getField(msg, $index$)", "index", JSFieldIndex(field)); @@ -1860,6 +2078,40 @@ void Generator::GenerateClassFields(const GeneratorOptions& options, } } +void GenerateBytesWrapper(const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* field, + BytesMode bytes_mode) { + string type = + JSFieldTypeAnnotation(options, field, + /* force_optional = */ false, + /* force_present = */ !HasFieldPresence(field), + /* singular_if_not_packed = */ false, + bytes_mode); + printer->Print( + "/**\n" + " * $fielddef$\n" + "$comment$" + " * This is a type-conversion wrapper around `get$defname$()`\n" + " * @return {$type$}\n" + " */\n" + "$class$.prototype.get$name$ = function() {\n" + " return /** @type {$type$} */ (jspb.Message.bytes$list$As$suffix$(\n" + " this.get$defname$()));\n" + "};\n" + "\n" + "\n", + "fielddef", FieldDefinition(options, field), + "comment", FieldComments(field, bytes_mode), + "type", type, + "class", GetPath(options, field->containing_type()), + "name", JSGetterName(field, bytes_mode), + "list", field->is_repeated() ? "List" : "", + "suffix", JSByteGetterSuffix(bytes_mode), + "defname", JSGetterName(field, BYTES_DEFAULT)); +} + + void Generator::GenerateClassField(const GeneratorOptions& options, io::Printer* printer, const FieldDescriptor* field) const { @@ -1871,12 +2123,11 @@ void Generator::GenerateClassField(const GeneratorOptions& options, " * @return {$type$}\n" " */\n", "fielddef", FieldDefinition(options, field), - "comment", FieldComments(field), + "comment", FieldComments(field, BYTES_DEFAULT), "type", JSFieldTypeAnnotation(options, field, /* force_optional = */ false, /* force_present = */ false, - /* singular_if_not_packed = */ false, - /* always_singular = */ false)); + /* singular_if_not_packed = */ false)); printer->Print( "$class$.prototype.get$name$ = function() {\n" " return /** @type{$type$} */ (\n" @@ -1890,8 +2141,7 @@ void Generator::GenerateClassField(const GeneratorOptions& options, "type", JSFieldTypeAnnotation(options, field, /* force_optional = */ false, /* force_present = */ false, - /* singular_if_not_packed = */ false, - /* always_singular = */ false), + /* singular_if_not_packed = */ false), "rpt", (field->is_repeated() ? "Repeated" : ""), "index", JSFieldIndex(field), "wrapperclass", SubmessageTypeRef(options, field), @@ -1905,8 +2155,7 @@ void Generator::GenerateClassField(const GeneratorOptions& options, JSFieldTypeAnnotation(options, field, /* force_optional = */ true, /* force_present = */ false, - /* singular_if_not_packed = */ false, - /* always_singular = */ false), + /* singular_if_not_packed = */ false), "returndoc", JSReturnDoc(options, field), "class", GetPath(options, field->containing_type()), "name", JSGetterName(field), @@ -1935,15 +2184,29 @@ void Generator::GenerateClassField(const GeneratorOptions& options, "returnvalue", JSReturnClause(field)); } else { - string typed_annotation; + bool untyped = + false; // Simple (primitive) field, either singular or repeated. - { - typed_annotation = JSFieldTypeAnnotation(options, field, + + // TODO(b/26173701): Always use BYTES_DEFAULT for the getter return type; + // at this point we "lie" to non-binary users and tell the the return + // type is always base64 string, pending a LSC to migrate to typed getters. + BytesMode bytes_mode = + field->type() == FieldDescriptor::TYPE_BYTES && !options.binary ? + BYTES_B64 : BYTES_DEFAULT; + string typed_annotation = + JSFieldTypeAnnotation(options, field, /* force_optional = */ false, /* force_present = */ !HasFieldPresence(field), /* singular_if_not_packed = */ false, - /* always_singular = */ false), + /* bytes_mode = */ bytes_mode); + if (untyped) { + printer->Print( + "/**\n" + " * @return {?} Raw field, untyped.\n" + " */\n"); + } else { printer->Print( "/**\n" " * $fielddef$\n" @@ -1951,7 +2214,7 @@ void Generator::GenerateClassField(const GeneratorOptions& options, " * @return {$type$}\n" " */\n", "fielddef", FieldDefinition(options, field), - "comment", FieldComments(field), + "comment", FieldComments(field, bytes_mode), "type", typed_annotation); } @@ -1960,7 +2223,10 @@ void Generator::GenerateClassField(const GeneratorOptions& options, "class", GetPath(options, field->containing_type()), "name", JSGetterName(field)); - { + if (untyped) { + printer->Print( + " return "); + } else { printer->Print( " return /** @type {$type$} */ (", "type", typed_annotation); @@ -1975,17 +2241,39 @@ void Generator::GenerateClassField(const GeneratorOptions& options, "default", Proto3PrimitiveFieldDefault(field)); } else { if (field->has_default_value()) { - printer->Print("jspb.Message.getField(this, $index$) != null ? " - "jspb.Message.getField(this, $index$) : $defaultValue$", + printer->Print("jspb.Message.getField(this, $index$) == null ? " + "$defaultValue$ : ", "index", JSFieldIndex(field), "defaultValue", JSFieldDefault(field)); + } + if (field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT || + field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE) { + if (field->is_repeated()) { + printer->Print("jspb.Message.getRepeatedFloatingPointField(" + "this, $index$)", + "index", JSFieldIndex(field)); + } else if (field->is_optional() && !field->has_default_value()) { + printer->Print("jspb.Message.getOptionalFloatingPointField(" + "this, $index$)", + "index", JSFieldIndex(field)); + } else { + // Convert "NaN" to NaN. + printer->Print("+jspb.Message.getField(this, $index$)", + "index", JSFieldIndex(field)); + } } else { printer->Print("jspb.Message.getField(this, $index$)", "index", JSFieldIndex(field)); } } - { + if (untyped) { + printer->Print( + ";\n" + "};\n" + "\n" + "\n"); + } else { printer->Print( ");\n" "};\n" @@ -1993,18 +2281,27 @@ void Generator::GenerateClassField(const GeneratorOptions& options, "\n"); } - { + if (field->type() == FieldDescriptor::TYPE_BYTES && !untyped) { + GenerateBytesWrapper(options, printer, field, BYTES_B64); + GenerateBytesWrapper(options, printer, field, BYTES_U8); + } + + if (untyped) { + printer->Print( + "/**\n" + " * @param {*} value $returndoc$\n" + " */\n", + "returndoc", JSReturnDoc(options, field)); + } else { printer->Print( "/** @param {$optionaltype$} value $returndoc$ */\n", "optionaltype", JSFieldTypeAnnotation(options, field, /* force_optional = */ true, /* force_present = */ !HasFieldPresence(field), - /* singular_if_not_packed = */ false, - /* always_singular = */ false), + /* singular_if_not_packed = */ false), "returndoc", JSReturnDoc(options, field)); } - printer->Print( "$class$.prototype.set$name$ = function(value) {\n" " jspb.Message.set$oneoftag$Field(this, $index$", @@ -2017,14 +2314,22 @@ void Generator::GenerateClassField(const GeneratorOptions& options, "};\n" "\n" "\n", - "type", "", - "typeclose", "", + "type", + untyped ? "/** @type{string|number|boolean|Array|undefined} */(" : "", + "typeclose", untyped ? ")" : "", "oneofgroup", (field->containing_oneof() ? (", " + JSOneofArray(options, field)) : ""), "returnvalue", JSReturnClause(field), "rptvalueinit", (field->is_repeated() ? " || []" : "")); + if (untyped) { + printer->Print( + "/**\n" + " * Clears the value. $returndoc$\n" + " */\n", + "returndoc", JSReturnDoc(options, field)); + } if (HasFieldPresence(field)) { printer->Print( @@ -2162,16 +2467,18 @@ void Generator::GenerateClassDeserializeBinaryField( " var value = /** @type {$fieldtype$} */ (reader.$reader$());\n", "fieldtype", JSFieldTypeAnnotation(options, field, false, true, /* singular_if_not_packed = */ true, - /* always_singular = */ false), + BYTES_U8), "reader", JSBinaryReaderMethodName(field)); } if (field->is_repeated() && !field->is_packed()) { // Repeated fields receive a |value| one at at a time; append to array - // returned by get$name$(). - printer->Print( - " msg.get$name$().push(value);\n", - "name", JSGetterName(field)); + // returned by get$name$(). Annoyingly, we have to call 'set' after + // changing the array. + printer->Print(" msg.get$name$().push(value);\n", "name", + JSGetterName(field)); + printer->Print(" msg.set$name$(msg.get$name$());\n", "name", + JSGetterName(field)); } else { // Singular fields, and packed repeated fields, receive a |value| either as // the field's value or as the array of all the field's values; set this as @@ -2244,7 +2551,7 @@ void Generator::GenerateClassSerializeBinaryField( const FieldDescriptor* field) const { printer->Print( " f = this.get$name$();\n", - "name", JSGetterName(field)); + "name", JSGetterName(field, BYTES_U8)); if (field->is_repeated()) { printer->Print( @@ -2294,7 +2601,6 @@ void Generator::GenerateClassSerializeBinaryField( " $index$,\n" " f", "writer", JSBinaryWriterMethodName(field), - "name", JSGetterName(field), "index", SimpleItoa(field->number())); if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { @@ -2356,8 +2662,7 @@ void Generator::GenerateExtension(const GeneratorOptions& options, options, field, /* force_optional = */ false, /* force_present = */ true, - /* singular_if_not_packed = */ false, - /* always_singular = */ false)); + /* singular_if_not_packed = */ false)); printer->Print( " $index$,\n" " {$name$: 0},\n" @@ -2528,7 +2833,7 @@ void Generator::GenerateFile(const GeneratorOptions& options, printer->Print("var global = Function('return this')();\n\n"); for (int i = 0; i < file->dependency_count(); i++) { - const std::string& name = file->dependency(i)->name(); + const string& name = file->dependency(i)->name(); printer->Print( "var $alias$ = require('$file$');\n", "alias", ModuleAlias(name), @@ -2543,7 +2848,7 @@ void Generator::GenerateFile(const GeneratorOptions& options, // // // Later generated code expects foo.bar = {} to exist: // foo.bar.Baz = function() { /* ... */ } - std::set provided; + set provided; // Cover the case where this file declares extensions but no messages. // This will ensure that the file-level object will be declared to hold @@ -2615,7 +2920,7 @@ bool Generator::GenerateAll(const vector& files, FindProvidesForFields(options, &printer, extensions, &provided); GenerateProvides(options, &printer, &provided); GenerateTestOnly(options, &printer); - GenerateRequires(options, &printer, files, &provided); + GenerateRequiresForLibrary(options, &printer, files, &provided); GenerateFilesInDepOrder(options, &printer, files); @@ -2629,66 +2934,20 @@ bool Generator::GenerateAll(const vector& files, return false; } } else if (options.import_style == GeneratorOptions::IMPORT_CLOSURE) { - // Collect all types, and print each type to a separate file. Pull out - // free-floating extensions while we make this pass. - map< string, vector > extensions_by_namespace; - - // If we're generating code in file-per-type mode, avoid overwriting files - // by choosing the last descriptor that writes each filename and permitting - // only those to generate code. - - // Current descriptor that will generate each filename, indexed by filename. - map desc_by_filename; - // Set of descriptors allowed to generate files. - set allowed_descs; - - for (int i = 0; i < files.size(); i++) { - // Collect all (descriptor, filename) pairs. - map descs_in_file; - for (int j = 0; j < files[i]->message_type_count(); j++) { - const Descriptor* desc = files[i]->message_type(j); - string filename = - options.output_dir + "/" + ToFileName(desc->name()) + ".js"; - descs_in_file[desc] = filename; - } - for (int j = 0; j < files[i]->enum_type_count(); j++) { - const EnumDescriptor* desc = files[i]->enum_type(j); - string filename = - options.output_dir + "/" + ToFileName(desc->name()) + ".js"; - descs_in_file[desc] = filename; - } - - // For each (descriptor, filename) pair, update the - // descriptors-by-filename map, and if a previous descriptor was already - // writing the filename, remove it from the allowed-descriptors set. - map::iterator it; - for (it = descs_in_file.begin(); it != descs_in_file.end(); ++it) { - const void* desc = it->first; - const string& filename = it->second; - if (desc_by_filename.find(filename) != desc_by_filename.end()) { - if (options.error_on_name_conflict) { - *error = "Name conflict: file name " + filename + - " would be generated by two descriptors"; - return false; - } - allowed_descs.erase(desc_by_filename[filename]); - } - desc_by_filename[filename] = desc; - allowed_descs.insert(desc); - } + set allowed_set; + if (!GenerateJspbAllowedSet(options, files, &allowed_set, error)) { + return false; } - // Generate code. for (int i = 0; i < files.size(); i++) { const FileDescriptor* file = files[i]; for (int j = 0; j < file->message_type_count(); j++) { const Descriptor* desc = file->message_type(j); - if (allowed_descs.find(desc) == allowed_descs.end()) { + if (allowed_set.count(desc) == 0) { continue; } - string filename = options.output_dir + "/" + - ToFileName(desc->name()) + ".js"; + string filename = GetMessageFileName(options, desc); google::protobuf::scoped_ptr output( context->Open(filename)); GOOGLE_CHECK(output.get()); @@ -2700,7 +2959,7 @@ bool Generator::GenerateAll(const vector& files, FindProvidesForMessage(options, &printer, desc, &provided); GenerateProvides(options, &printer, &provided); GenerateTestOnly(options, &printer); - GenerateRequires(options, &printer, desc, &provided); + GenerateRequiresForMessage(options, &printer, desc, &provided); GenerateClass(options, &printer, desc); @@ -2710,13 +2969,11 @@ bool Generator::GenerateAll(const vector& files, } for (int j = 0; j < file->enum_type_count(); j++) { const EnumDescriptor* enumdesc = file->enum_type(j); - if (allowed_descs.find(enumdesc) == allowed_descs.end()) { + if (allowed_set.count(enumdesc) == 0) { continue; } - string filename = options.output_dir + "/" + - ToFileName(enumdesc->name()) + ".js"; - + string filename = GetEnumFileName(options, enumdesc); google::protobuf::scoped_ptr output( context->Open(filename)); GOOGLE_CHECK(output.get()); @@ -2735,38 +2992,36 @@ bool Generator::GenerateAll(const vector& files, return false; } } - // Pull out all free-floating extensions and generate files for those too. - for (int j = 0; j < file->extension_count(); j++) { - const FieldDescriptor* extension = file->extension(j); - extensions_by_namespace[GetPath(options, files[i])] - .push_back(extension); - } - } + // File-level extensions (message-level extensions are generated under + // the enclosing message). + if (allowed_set.count(file) == 1) { + string filename = GetExtensionFileName(options, file); - // Generate extensions in separate files. - map< string, vector >::iterator it; - for (it = extensions_by_namespace.begin(); - it != extensions_by_namespace.end(); - ++it) { - string filename = options.output_dir + "/" + - ToFileName(it->first) + ".js"; + google::protobuf::scoped_ptr output( + context->Open(filename)); + GOOGLE_CHECK(output.get()); + io::Printer printer(output.get(), '$'); - google::protobuf::scoped_ptr output( - context->Open(filename)); - GOOGLE_CHECK(output.get()); - io::Printer printer(output.get(), '$'); + GenerateHeader(options, &printer); - GenerateHeader(options, &printer); + std::set provided; + vector fields; - std::set provided; - FindProvidesForFields(options, &printer, it->second, &provided); - GenerateProvides(options, &printer, &provided); - GenerateTestOnly(options, &printer); - GenerateRequires(options, &printer, it->second, &provided); + for (int j = 0; j < files[i]->extension_count(); j++) { + if (ShouldGenerateExtension(files[i]->extension(j))) { + fields.push_back(files[i]->extension(j)); + } + } - for (int j = 0; j < it->second.size(); j++) { - if (ShouldGenerateExtension(it->second[j])) { - GenerateExtension(options, &printer, it->second[j]); + FindProvidesForFields(options, &printer, fields, &provided); + GenerateProvides(options, &printer, &provided); + GenerateTestOnly(options, &printer); + GenerateRequiresForExtensions(options, &printer, fields, &provided); + + for (int j = 0; j < files[i]->extension_count(); j++) { + if (ShouldGenerateExtension(files[i]->extension(j))) { + GenerateExtension(options, &printer, files[i]->extension(j)); + } } } } @@ -2777,8 +3032,7 @@ bool Generator::GenerateAll(const vector& files, const google::protobuf::FileDescriptor* file = files[i]; string filename = options.output_dir + "/" + GetJSFilename(file->name()); - google::protobuf::scoped_ptr output( - context->Open(filename)); + google::protobuf::scoped_ptr output(context->Open(filename)); GOOGLE_CHECK(output.get()); io::Printer printer(output.get(), '$'); diff --git a/src/google/protobuf/compiler/js/js_generator.h b/src/google/protobuf/compiler/js/js_generator.h index db9178d34..6fd7ca507 100755 --- a/src/google/protobuf/compiler/js/js_generator.h +++ b/src/google/protobuf/compiler/js/js_generator.h @@ -146,19 +146,19 @@ class LIBPROTOC_EXPORT Generator : public CodeGenerator { io::Printer* printer) const; // Generate goog.requires() calls. - void GenerateRequires(const GeneratorOptions& options, - io::Printer* printer, - const vector& file, - std::set* provided) const; - void GenerateRequires(const GeneratorOptions& options, + void GenerateRequiresForLibrary(const GeneratorOptions& options, + io::Printer* printer, + const vector& files, + std::set* provided) const; + void GenerateRequiresForMessage(const GeneratorOptions& options, io::Printer* printer, const Descriptor* desc, std::set* provided) const; // For extension fields at file scope. - void GenerateRequires(const GeneratorOptions& options, - io::Printer* printer, - const vector& fields, - std::set* provided) const; + void GenerateRequiresForExtensions( + const GeneratorOptions& options, io::Printer* printer, + const vector& fields, + std::set* provided) const; void GenerateRequiresImpl(const GeneratorOptions& options, io::Printer* printer, std::set* required, diff --git a/src/google/protobuf/compiler/mock_code_generator.cc b/src/google/protobuf/compiler/mock_code_generator.cc index 121d917b9..82bb34274 100644 --- a/src/google/protobuf/compiler/mock_code_generator.cc +++ b/src/google/protobuf/compiler/mock_code_generator.cc @@ -32,20 +32,26 @@ #include +#include +#include #include #ifndef _SHARED_PTR_H #include #endif +#include +#include +#include #include +#include +#include +#include +#include #include #include -#include -#include #include #include #include -#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/compiler/mock_code_generator.h b/src/google/protobuf/compiler/mock_code_generator.h index 8c8348d85..e1665f88c 100644 --- a/src/google/protobuf/compiler/mock_code_generator.h +++ b/src/google/protobuf/compiler/mock_code_generator.h @@ -34,9 +34,14 @@ #define GOOGLE_PROTOBUF_COMPILER_MOCK_CODE_GENERATOR_H__ #include + #include namespace google { +namespace protobuf { +class FileDescriptor; +} // namespace protobuf + namespace protobuf { namespace compiler { diff --git a/src/google/protobuf/compiler/objectivec/objectivec_enum.cc b/src/google/protobuf/compiler/objectivec/objectivec_enum.cc index 857d24a41..e76f8e991 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_enum.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_enum.cc @@ -72,8 +72,9 @@ void EnumGenerator::GenerateHeader(io::Printer* printer) { "\n", "name", name_); - printer->Print("$comments$typedef GPB_ENUM($name$) {\n", + printer->Print("$comments$typedef$deprecated_attribute$ GPB_ENUM($name$) {\n", "comments", enum_comments, + "deprecated_attribute", GetOptionalDeprecatedAttribute(descriptor_), "name", name_); printer->Indent(); @@ -99,8 +100,9 @@ void EnumGenerator::GenerateHeader(io::Printer* printer) { } printer->Print( - "$name$ = $value$,\n", + "$name$$deprecated_attribute$ = $value$,\n", "name", EnumValueName(all_values_[i]), + "deprecated_attribute", GetOptionalDeprecatedAttribute(all_values_[i]), "value", SimpleItoa(all_values_[i]->number())); } printer->Outdent(); @@ -122,16 +124,6 @@ void EnumGenerator::GenerateSource(io::Printer* printer) { "\n", "name", name_); - printer->Print( - "GPBEnumDescriptor *$name$_EnumDescriptor(void) {\n" - " static GPBEnumDescriptor *descriptor = NULL;\n" - " if (!descriptor) {\n" - " static GPBMessageEnumValueDescription values[] = {\n", - "name", name_); - printer->Indent(); - printer->Indent(); - printer->Indent(); - // Note: For the TextFormat decode info, we can't use the enum value as // the key because protocol buffer enums have 'allow_alias', which lets // a value be used more than once. Instead, the index into the list of @@ -139,41 +131,66 @@ void EnumGenerator::GenerateSource(io::Printer* printer) { // will be zero. TextFormatDecodeData text_format_decode_data; int enum_value_description_key = -1; + string text_blob; for (int i = 0; i < all_values_.size(); i++) { ++enum_value_description_key; string short_name(EnumValueShortName(all_values_[i])); - printer->Print("{ .name = \"$short_name$\", .number = $name$ },\n", - "short_name", short_name, - "name", EnumValueName(all_values_[i])); + text_blob += short_name + '\0'; if (UnCamelCaseEnumShortName(short_name) != all_values_[i]->name()) { text_format_decode_data.AddString(enum_value_description_key, short_name, all_values_[i]->name()); } } - printer->Outdent(); - printer->Outdent(); - printer->Outdent(); + + printer->Print( + "GPBEnumDescriptor *$name$_EnumDescriptor(void) {\n" + " static GPBEnumDescriptor *descriptor = NULL;\n" + " if (!descriptor) {\n", + "name", name_); + + static const int kBytesPerLine = 40; // allow for escaping + printer->Print( + " static const char *valueNames ="); + for (int i = 0; i < text_blob.size(); i += kBytesPerLine) { + printer->Print( + "\n \"$data$\"", + "data", EscapeTrigraphs(CEscape(text_blob.substr(i, kBytesPerLine)))); + } + printer->Print( + ";\n" + " static const int32_t values[] = {\n"); + for (int i = 0; i < all_values_.size(); i++) { + printer->Print(" $name$,\n", "name", EnumValueName(all_values_[i])); + } printer->Print(" };\n"); + if (text_format_decode_data.num_entries() == 0) { printer->Print( - " descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol($name$)\n" - " values:values\n" - " valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription)\n" - " enumVerifier:$name$_IsValidValue];\n", + " GPBEnumDescriptor *worker =\n" + " [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol($name$)\n" + " valueNames:valueNames\n" + " values:values\n" + " count:(uint32_t)(sizeof(values) / sizeof(int32_t))\n" + " enumVerifier:$name$_IsValidValue];\n", "name", name_); } else { printer->Print( " static const char *extraTextFormatInfo = \"$extraTextFormatInfo$\";\n" - " descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol($name$)\n" - " values:values\n" - " valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription)\n" - " enumVerifier:$name$_IsValidValue\n" - " extraTextFormatInfo:extraTextFormatInfo];\n", + " GPBEnumDescriptor *worker =\n" + " [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol($name$)\n" + " valueNames:valueNames\n" + " values:values\n" + " count:(uint32_t)(sizeof(values) / sizeof(int32_t))\n" + " enumVerifier:$name$_IsValidValue\n" + " extraTextFormatInfo:extraTextFormatInfo];\n", "name", name_, "extraTextFormatInfo", CEscape(text_format_decode_data.Data())); } printer->Print( + " if (!OSAtomicCompareAndSwapPtrBarrier(nil, worker, (void * volatile *)&descriptor)) {\n" + " [worker release];\n" + " }\n" " }\n" " return descriptor;\n" "}\n\n"); diff --git a/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc index cfbb8c525..b63bc0de6 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc @@ -59,6 +59,9 @@ void SetEnumVariables(const FieldDescriptor* descriptor, (*variables)["enum_verifier"] = type + "_IsValidValue"; (*variables)["enum_desc_func"] = type + "_EnumDescriptor"; + (*variables)["dataTypeSpecific_name"] = "enumDescFunc"; + (*variables)["dataTypeSpecific_value"] = (*variables)["enum_desc_func"]; + const Descriptor* msg_descriptor = descriptor->containing_type(); (*variables)["owning_message_class"] = ClassName(msg_descriptor); } @@ -72,13 +75,6 @@ EnumFieldGenerator::EnumFieldGenerator(const FieldDescriptor* descriptor, EnumFieldGenerator::~EnumFieldGenerator() {} -void EnumFieldGenerator::GenerateFieldDescriptionTypeSpecific( - io::Printer* printer) const { - printer->Print( - variables_, - " .dataTypeSpecific.enumDescFunc = $enum_desc_func$,\n"); -} - void EnumFieldGenerator::GenerateCFunctionDeclarations( io::Printer* printer) const { if (!HasPreservingUnknownEnumSemantics(descriptor_->file())) { @@ -145,13 +141,6 @@ void RepeatedEnumFieldGenerator::FinishInitialization(void) { "// |" + variables_["name"] + "| contains |" + variables_["storage_type"] + "|\n"; } -void RepeatedEnumFieldGenerator::GenerateFieldDescriptionTypeSpecific( - io::Printer* printer) const { - printer->Print( - variables_, - " .dataTypeSpecific.enumDescFunc = $enum_desc_func$,\n"); -} - } // namespace objectivec } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/objectivec/objectivec_enum_field.h b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.h index ae2f57e3c..946faa819 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_enum_field.h +++ b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.h @@ -45,7 +45,6 @@ class EnumFieldGenerator : public SingleFieldGenerator { const Options& options); public: - virtual void GenerateFieldDescriptionTypeSpecific(io::Printer* printer) const; virtual void GenerateCFunctionDeclarations(io::Printer* printer) const; virtual void GenerateCFunctionImplementations(io::Printer* printer) const; virtual void DetermineForwardDeclarations(set* fwd_decls) const; @@ -64,7 +63,6 @@ class RepeatedEnumFieldGenerator : public RepeatedFieldGenerator { public: virtual void FinishInitialization(); - virtual void GenerateFieldDescriptionTypeSpecific(io::Printer* printer) const; protected: RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, diff --git a/src/google/protobuf/compiler/objectivec/objectivec_extension.cc b/src/google/protobuf/compiler/objectivec/objectivec_extension.cc index 4e348393a..3f7ab9d39 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_extension.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_extension.cc @@ -114,14 +114,14 @@ void ExtensionGenerator::GenerateStaticVariablesInitialization( printer->Print(vars, "{\n" - " .singletonName = GPBStringifySymbol($root_class_and_method_name$),\n" - " .dataType = $extension_type$,\n" - " .extendedClass = GPBStringifySymbol($extended_type$),\n" - " .fieldNumber = $number$,\n" " .defaultValue.$default_name$ = $default$,\n" + " .singletonName = GPBStringifySymbol($root_class_and_method_name$),\n" + " .extendedClass = GPBStringifySymbol($extended_type$),\n" " .messageOrGroupClassName = $type$,\n" - " .options = $options$,\n" " .enumDescriptorFunc = $enum_desc_func_name$,\n" + " .fieldNumber = $number$,\n" + " .dataType = $extension_type$,\n" + " .options = $options$,\n" "},\n"); } diff --git a/src/google/protobuf/compiler/objectivec/objectivec_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_field.cc index 8697e2250..66cb4a160 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_field.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_field.cc @@ -28,6 +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. +#include + #include #include #include @@ -75,8 +77,8 @@ void SetCommonFieldVariables(const FieldDescriptor* descriptor, (*variables)["field_number_name"] = classname + "_FieldNumber_" + capitalized_name; (*variables)["field_number"] = SimpleItoa(descriptor->number()); - (*variables)["has_index"] = SimpleItoa(descriptor->index()); (*variables)["field_type"] = GetCapitalizedType(descriptor); + (*variables)["deprecated_attribute"] = GetOptionalDeprecatedAttribute(descriptor); std::vector field_flags; if (descriptor->is_repeated()) field_flags.push_back("GPBFieldRepeated"); if (descriptor->is_required()) field_flags.push_back("GPBFieldRequired"); @@ -99,18 +101,9 @@ void SetCommonFieldVariables(const FieldDescriptor* descriptor, (*variables)["dataTypeSpecific_name"] = "className"; (*variables)["dataTypeSpecific_value"] = "NULL"; - string field_options = descriptor->options().SerializeAsString(); - // Must convert to a standard byte order for packing length into - // a cstring. - uint32 length = ghtonl(field_options.length()); - if (length > 0) { - string bytes((const char*)&length, sizeof(length)); - bytes.append(field_options); - string options_str = "\"" + CEscape(bytes) + "\""; - (*variables)["fieldoptions"] = "\"" + CEscape(bytes) + "\""; - } else { - (*variables)["fieldoptions"] = ""; - } + (*variables)["storage_offset_value"] = + "(uint32_t)offsetof(" + classname + "__storage_, " + camel_case_name + ")"; + (*variables)["storage_offset_comment"] = ""; // Clear some common things so they can be set just when needed. (*variables)["storage_attribute"] = ""; @@ -190,52 +183,54 @@ void FieldGenerator::DetermineForwardDeclarations( } void FieldGenerator::GenerateFieldDescription( - io::Printer* printer) const { - printer->Print( - variables_, - "{\n" - " .name = \"$name$\",\n" - " .number = $field_number_name$,\n" - " .hasIndex = $has_index$,\n" - " .flags = $fieldflags$,\n" - " .dataType = GPBDataType$field_type$,\n" - " .offset = offsetof($classname$__storage_, $name$),\n" - " .defaultValue.$default_name$ = $default$,\n"); - - // TODO(thomasvl): It might be useful to add a CPP wrapper to support - // compiling away the EnumDescriptors. To do that, we'd need a #if here - // to control setting the descriptor vs. the validator, and above in - // SetCommonFieldVariables() we'd want to wrap how we add - // GPBFieldHasDefaultValue to the flags. - - // " .dataTypeSpecific.value* = [something]," - GenerateFieldDescriptionTypeSpecific(printer); - - const string& field_options(variables_.find("fieldoptions")->second); - if (field_options.empty()) { - printer->Print(" .fieldOptions = NULL,\n"); - } else { - // Can't use PrintRaw() here to get the #if/#else/#endif lines completely - // outdented because the need for indent captured on the previous - // printing of a \n and there is no way to get the current indent level - // to call the right number of Outdent()/Indents() to maintain state. + io::Printer* printer, bool include_default) const { + // Printed in the same order as the structure decl. + if (include_default) { printer->Print( variables_, - "#if GPBOBJC_INCLUDE_FIELD_OPTIONS\n" - " .fieldOptions = $fieldoptions$,\n" - "#else\n" - " .fieldOptions = NULL,\n" - "#endif // GPBOBJC_INCLUDE_FIELD_OPTIONS\n"); + "{\n" + " .defaultValue.$default_name$ = $default$,\n" + " .core.name = \"$name$\",\n" + " .core.dataTypeSpecific.$dataTypeSpecific_name$ = $dataTypeSpecific_value$,\n" + " .core.number = $field_number_name$,\n" + " .core.hasIndex = $has_index$,\n" + " .core.offset = $storage_offset_value$,$storage_offset_comment$\n" + " .core.flags = $fieldflags$,\n" + " .core.dataType = GPBDataType$field_type$,\n" + "},\n"); + } else { + printer->Print( + variables_, + "{\n" + " .name = \"$name$\",\n" + " .dataTypeSpecific.$dataTypeSpecific_name$ = $dataTypeSpecific_value$,\n" + " .number = $field_number_name$,\n" + " .hasIndex = $has_index$,\n" + " .offset = $storage_offset_value$,$storage_offset_comment$\n" + " .flags = $fieldflags$,\n" + " .dataType = GPBDataType$field_type$,\n" + "},\n"); } - - printer->Print("},\n"); } -void FieldGenerator::GenerateFieldDescriptionTypeSpecific( - io::Printer* printer) const { - printer->Print( - variables_, - " .dataTypeSpecific.$dataTypeSpecific_name$ = $dataTypeSpecific_value$,\n"); +void FieldGenerator::SetRuntimeHasBit(int has_index) { + variables_["has_index"] = SimpleItoa(has_index); +} + +void FieldGenerator::SetNoHasBit(void) { + variables_["has_index"] = "GPBNoHasBit"; +} + +int FieldGenerator::ExtraRuntimeHasBitsNeeded(void) const { + return 0; +} + +void FieldGenerator::SetExtraRuntimeHasBitsBase(int index_base) { + // NOTE: src/google/protobuf/compiler/plugin.cc makes use of cerr for some + // error cases, so it seems to be ok to use as a back door for errors. + cerr << "Error: should have overriden SetExtraRuntimeHasBitsBase()." << endl; + cerr.flush(); + abort(); } void FieldGenerator::SetOneofIndexBase(int index_base) { @@ -272,12 +267,12 @@ void SingleFieldGenerator::GeneratePropertyDeclaration( printer->Print(variables_, "$comments$"); printer->Print( variables_, - "@property(nonatomic, readwrite) $property_type$ $name$;\n" + "@property(nonatomic, readwrite) $property_type$ $name$$deprecated_attribute$;\n" "\n"); if (WantsHasProperty()) { printer->Print( variables_, - "@property(nonatomic, readwrite) BOOL has$capitalized_name$;\n"); + "@property(nonatomic, readwrite) BOOL has$capitalized_name$$deprecated_attribute$;\n"); } } @@ -302,6 +297,14 @@ bool SingleFieldGenerator::WantsHasProperty(void) const { return false; } +bool SingleFieldGenerator::RuntimeUsesHasBit(void) const { + if (descriptor_->containing_oneof() != NULL) { + // The oneof tracks what is set instead. + return false; + } + return true; +} + ObjCObjFieldGenerator::ObjCObjFieldGenerator(const FieldDescriptor* descriptor, const Options& options) : SingleFieldGenerator(descriptor, options) { @@ -328,18 +331,18 @@ void ObjCObjFieldGenerator::GeneratePropertyDeclaration( printer->Print(variables_, "$comments$"); printer->Print( variables_, - "@property(nonatomic, readwrite, $property_storage_attribute$, null_resettable) $property_type$ *$name$$storage_attribute$;\n"); + "@property(nonatomic, readwrite, $property_storage_attribute$, null_resettable) $property_type$ *$name$$storage_attribute$$deprecated_attribute$;\n"); if (WantsHasProperty()) { printer->Print( variables_, "/// Test to see if @c $name$ has been set.\n" - "@property(nonatomic, readwrite) BOOL has$capitalized_name$;\n"); + "@property(nonatomic, readwrite) BOOL has$capitalized_name$$deprecated_attribute$;\n"); } if (IsInitName(variables_.find("name")->second)) { // If property name starts with init we need to annotate it to get past ARC. // http://stackoverflow.com/questions/18723226/how-do-i-annotate-an-objective-c-property-with-an-objc-method-family/18723227#18723227 printer->Print(variables_, - "- ($property_type$ *)$name$ GPB_METHOD_FAMILY_NONE;\n"); + "- ($property_type$ *)$name$ GPB_METHOD_FAMILY_NONE$deprecated_attribute$;\n"); } printer->Print("\n"); } @@ -347,8 +350,6 @@ void ObjCObjFieldGenerator::GeneratePropertyDeclaration( RepeatedFieldGenerator::RepeatedFieldGenerator( const FieldDescriptor* descriptor, const Options& options) : ObjCObjFieldGenerator(descriptor, options) { - // Repeated fields don't use the has index. - variables_["has_index"] = "GPBNoHasBit"; // Default to no comment and let the cases needing it fill it in. variables_["array_comment"] = ""; } @@ -385,14 +386,14 @@ void RepeatedFieldGenerator::GeneratePropertyDeclaration( variables_, "$comments$" "$array_comment$" - "@property(nonatomic, readwrite, strong, null_resettable) $array_property_type$ *$name$$storage_attribute$;\n" + "@property(nonatomic, readwrite, strong, null_resettable) $array_property_type$ *$name$$storage_attribute$$deprecated_attribute$;\n" "/// The number of items in @c $name$ without causing the array to be created.\n" - "@property(nonatomic, readonly) NSUInteger $name$_Count;\n"); + "@property(nonatomic, readonly) NSUInteger $name$_Count$deprecated_attribute$;\n"); if (IsInitName(variables_.find("name")->second)) { // If property name starts with init we need to annotate it to get past ARC. // http://stackoverflow.com/questions/18723226/how-do-i-annotate-an-objective-c-property-with-an-objc-method-family/18723227#18723227 printer->Print(variables_, - "- ($array_property_type$ *)$name$ GPB_METHOD_FAMILY_NONE;\n"); + "- ($array_property_type$ *)$name$ GPB_METHOD_FAMILY_NONE$deprecated_attribute$;\n"); } printer->Print("\n"); } @@ -402,6 +403,10 @@ bool RepeatedFieldGenerator::WantsHasProperty(void) const { return false; } +bool RepeatedFieldGenerator::RuntimeUsesHasBit(void) const { + return false; // The array having anything is what is used. +} + FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor, const Options& options) : descriptor_(descriptor), @@ -432,12 +437,40 @@ const FieldGenerator& FieldGeneratorMap::get_extension(int index) const { return *extension_generators_[index]; } +int FieldGeneratorMap::CalculateHasBits(void) { + int total_bits = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + if (field_generators_[i]->RuntimeUsesHasBit()) { + field_generators_[i]->SetRuntimeHasBit(total_bits); + ++total_bits; + } else { + field_generators_[i]->SetNoHasBit(); + } + int extra_bits = field_generators_[i]->ExtraRuntimeHasBitsNeeded(); + if (extra_bits) { + field_generators_[i]->SetExtraRuntimeHasBitsBase(total_bits); + total_bits += extra_bits; + } + } + return total_bits; +} + void FieldGeneratorMap::SetOneofIndexBase(int index_base) { for (int i = 0; i < descriptor_->field_count(); i++) { field_generators_[i]->SetOneofIndexBase(index_base); } } +bool FieldGeneratorMap::DoesAnyFieldHaveNonZeroDefault(void) const { + for (int i = 0; i < descriptor_->field_count(); i++) { + if (HasNonZeroDefaultValue(descriptor_->field(i))) { + return true; + } + } + + return false; +} + } // namespace objectivec } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/objectivec/objectivec_field.h b/src/google/protobuf/compiler/objectivec/objectivec_field.h index e8a20a72d..a3a4b1b6e 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_field.h +++ b/src/google/protobuf/compiler/objectivec/objectivec_field.h @@ -61,7 +61,6 @@ class FieldGenerator { // Called by GenerateFieldDescription, exposed for classes that need custom // generation. - virtual void GenerateFieldDescriptionTypeSpecific(io::Printer* printer) const; // Exposed for subclasses to extend, base does nothing. virtual void GenerateCFunctionDeclarations(io::Printer* printer) const; @@ -71,9 +70,16 @@ class FieldGenerator { virtual void DetermineForwardDeclarations(set* fwd_decls) const; // Used during generation, not intended to be extended by subclasses. - void GenerateFieldDescription(io::Printer* printer) const; + void GenerateFieldDescription( + io::Printer* printer, bool include_default) const; void GenerateFieldNumberConstant(io::Printer* printer) const; + // Exposed to get and set the has bits information. + virtual bool RuntimeUsesHasBit(void) const = 0; + void SetRuntimeHasBit(int has_index); + void SetNoHasBit(void); + virtual int ExtraRuntimeHasBitsNeeded(void) const; + virtual void SetExtraRuntimeHasBitsBase(int index_base); void SetOneofIndexBase(int index_base); string variable(const char* key) const { @@ -109,6 +115,8 @@ class SingleFieldGenerator : public FieldGenerator { virtual void GeneratePropertyImplementation(io::Printer* printer) const; + virtual bool RuntimeUsesHasBit(void) const; + protected: SingleFieldGenerator(const FieldDescriptor* descriptor, const Options& options); @@ -143,6 +151,8 @@ class RepeatedFieldGenerator : public ObjCObjFieldGenerator { virtual void GeneratePropertyImplementation(io::Printer* printer) const; + virtual bool RuntimeUsesHasBit(void) const; + protected: RepeatedFieldGenerator(const FieldDescriptor* descriptor, const Options& options); @@ -162,8 +172,14 @@ class FieldGeneratorMap { const FieldGenerator& get(const FieldDescriptor* field) const; const FieldGenerator& get_extension(int index) const; + // Assigns the has bits and returns the number of bits needed. + int CalculateHasBits(void); + void SetOneofIndexBase(int index_base); + // Check if any field of this message has a non zero default. + bool DoesAnyFieldHaveNonZeroDefault(void) const; + private: const Descriptor* descriptor_; scoped_array > field_generators_; diff --git a/src/google/protobuf/compiler/objectivec/objectivec_file.cc b/src/google/protobuf/compiler/objectivec/objectivec_file.cc index 161998840..ed4fc6a3a 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_file.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_file.cc @@ -45,7 +45,7 @@ namespace protobuf { // This is also found in GPBBootstrap.h, and needs to be kept in sync. It // is the version check done to ensure generated code works with the current // runtime being used. -const int32 GOOGLE_PROTOBUF_OBJC_GEN_VERSION = 30000; +const int32 GOOGLE_PROTOBUF_OBJC_GEN_VERSION = 30001; namespace compiler { namespace objectivec { @@ -115,6 +115,9 @@ void FileGenerator::GenerateHeader(io::Printer *printer) { printer->Print( "// @@protoc_insertion_point(imports)\n" "\n" + "#pragma clang diagnostic push\n" + "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n" + "\n" "CF_EXTERN_C_BEGIN\n" "\n"); @@ -189,6 +192,8 @@ void FileGenerator::GenerateHeader(io::Printer *printer) { "\n" "CF_EXTERN_C_END\n" "\n" + "#pragma clang diagnostic pop\n" + "\n" "// @@protoc_insertion_point(global_scope)\n"); } @@ -216,6 +221,9 @@ void FileGenerator::GenerateSource(io::Printer *printer) { } printer->Print( "// @@protoc_insertion_point(imports)\n" + "\n" + "#pragma clang diagnostic push\n" + "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n" "\n"); printer->Print( @@ -342,6 +350,8 @@ void FileGenerator::GenerateSource(io::Printer *printer) { } printer->Print( + "\n" + "#pragma clang diagnostic pop\n" "\n" "// @@protoc_insertion_point(global_scope)\n"); } diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc index 613a511cd..196b39dd9 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc @@ -220,11 +220,6 @@ string NameFromFieldDescriptor(const FieldDescriptor* field) { } } -// Escape C++ trigraphs by escaping question marks to \? -string EscapeTrigraphs(const string& to_escape) { - return StringReplace(to_escape, "?", "\\?", true); -} - void PathSplit(const string& path, string* directory, string* basename) { string::size_type last_slash = path.rfind('/'); if (last_slash == string::npos) { @@ -264,6 +259,11 @@ bool IsSpecialName(const string& name, const string* special_names, } // namespace +// Escape C++ trigraphs by escaping question marks to \? +string EscapeTrigraphs(const string& to_escape) { + return StringReplace(to_escape, "?", "\\?", true); +} + string StripProto(const string& filename) { if (HasSuffixString(filename, ".protodevel")) { return StripSuffixString(filename, ".protodevel"); @@ -734,7 +734,7 @@ string DefaultValue(const FieldDescriptor* field) { uint32 length = ghtonl(default_string.length()); string bytes((const char*)&length, sizeof(length)); bytes.append(default_string); - return "(NSData*)\"" + CEscape(bytes) + "\""; + return "(NSData*)\"" + EscapeTrigraphs(CEscape(bytes)) + "\""; } else { return "@\"" + EscapeTrigraphs(CEscape(default_string)) + "\""; } @@ -751,6 +751,51 @@ string DefaultValue(const FieldDescriptor* field) { return NULL; } +bool HasNonZeroDefaultValue(const FieldDescriptor* field) { + // Repeated fields don't have defaults. + if (field->is_repeated()) { + return false; + } + + // As much as checking field->has_default_value() seems useful, it isn't + // because of enums. proto2 syntax allows the first item in an enum (the + // default) to be non zero. So checking field->has_default_value() would + // result in missing this non zero default. See MessageWithOneBasedEnum in + // objectivec/Tests/unittest_objc.proto for a test Message to confirm this. + + // Some proto file set the default to the zero value, so make sure the value + // isn't the zero case. + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + return field->default_value_int32() != 0; + case FieldDescriptor::CPPTYPE_UINT32: + return field->default_value_uint32() != 0U; + case FieldDescriptor::CPPTYPE_INT64: + return field->default_value_int64() != 0LL; + case FieldDescriptor::CPPTYPE_UINT64: + return field->default_value_uint64() != 0ULL; + case FieldDescriptor::CPPTYPE_DOUBLE: + return field->default_value_double() != 0.0; + case FieldDescriptor::CPPTYPE_FLOAT: + return field->default_value_float() != 0.0f; + case FieldDescriptor::CPPTYPE_BOOL: + return field->default_value_bool(); + case FieldDescriptor::CPPTYPE_STRING: { + const string& default_string = field->default_value_string(); + return default_string.length() != 0; + } + case FieldDescriptor::CPPTYPE_ENUM: + return field->default_value_enum()->number() != 0; + case FieldDescriptor::CPPTYPE_MESSAGE: + return false; + } + + // Some compilers report reaching end of function even though all cases of + // the enum are handed in the switch. + GOOGLE_LOG(FATAL) << "Can't get here."; + return false; +} + string BuildFlagsString(const vector& strings) { if (strings.size() == 0) { return "0"; @@ -969,7 +1014,8 @@ bool ValidateObjCClassPrefix(const FileDescriptor* file, } else { // ...it didn't match! *out_error = "error: Expected 'option objc_class_prefix = \"" + - package_match->second + "\";' in '" + file->name() + "'"; + package_match->second + "\";' for package '" + package + + "' in '" + file->name() + "'"; if (prefix.length()) { *out_error += "; but found '" + prefix + "' instead"; } diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.h b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h index a301493e5..3f56d94b3 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_helpers.h +++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h @@ -48,6 +48,9 @@ struct Options { string expected_prefixes_path; }; +// Escape C++ trigraphs by escaping question marks to "\?". +string EscapeTrigraphs(const string& to_escape); + // Strips ".proto" or ".protodevel" from the end of a filename. string StripProto(const string& filename); @@ -130,6 +133,22 @@ enum ObjectiveCType { OBJECTIVECTYPE_MESSAGE }; +template +string GetOptionalDeprecatedAttribute(const TDescriptor* descriptor, bool preSpace = true, bool postNewline = false) { + if (descriptor->options().deprecated()) { + string result = "DEPRECATED_ATTRIBUTE"; + if (preSpace) { + result.insert(0, " "); + } + if (postNewline) { + result.append("\n"); + } + return result; + } else { + return ""; + } +} + string GetCapitalizedType(const FieldDescriptor* field); ObjectiveCType GetObjectiveCType(FieldDescriptor::Type field_type); @@ -143,6 +162,7 @@ bool IsReferenceType(const FieldDescriptor* field); string GPBGenericValueFieldName(const FieldDescriptor* field); string DefaultValue(const FieldDescriptor* field); +bool HasNonZeroDefaultValue(const FieldDescriptor* field); string BuildFlagsString(const vector& strings); diff --git a/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc index 2751e9369..ac5d8aea2 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc @@ -140,13 +140,18 @@ MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor, value_field_generator_->variable("storage_type") + "*>"; } } + + variables_["dataTypeSpecific_name"] = + value_field_generator_->variable("dataTypeSpecific_name"); + variables_["dataTypeSpecific_value"] = + value_field_generator_->variable("dataTypeSpecific_value"); } MapFieldGenerator::~MapFieldGenerator() {} void MapFieldGenerator::FinishInitialization(void) { RepeatedFieldGenerator::FinishInitialization(); - // Use the array_comment suport in RepeatedFieldGenerator to output what the + // Use the array_comment support in RepeatedFieldGenerator to output what the // values in the map are. const FieldDescriptor* value_descriptor = descriptor_->message_type()->FindFieldByName("value"); @@ -156,13 +161,6 @@ void MapFieldGenerator::FinishInitialization(void) { } } -void MapFieldGenerator::GenerateFieldDescriptionTypeSpecific( - io::Printer* printer) const { - // Relay it to the value generator to provide enum validator, message - // class, etc. - value_field_generator_->GenerateFieldDescriptionTypeSpecific(printer); -} - void MapFieldGenerator::DetermineForwardDeclarations( set* fwd_decls) const { RepeatedFieldGenerator::DetermineForwardDeclarations(fwd_decls); diff --git a/src/google/protobuf/compiler/objectivec/objectivec_map_field.h b/src/google/protobuf/compiler/objectivec/objectivec_map_field.h index 7351ea05f..bc68a6829 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_map_field.h +++ b/src/google/protobuf/compiler/objectivec/objectivec_map_field.h @@ -46,7 +46,6 @@ class MapFieldGenerator : public RepeatedFieldGenerator { public: virtual void FinishInitialization(void); - virtual void GenerateFieldDescriptionTypeSpecific(io::Printer* printer) const; protected: MapFieldGenerator(const FieldDescriptor* descriptor, const Options& options); diff --git a/src/google/protobuf/compiler/objectivec/objectivec_message.cc b/src/google/protobuf/compiler/objectivec/objectivec_message.cc index e0ea8bd27..bf272596c 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_message.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_message.cc @@ -66,11 +66,12 @@ int OrderGroupForFieldDescriptor(const FieldDescriptor* descriptor) { // The first item in the object structure is our uint32[] for has bits. // We then want to order things to make the instances as small as // possible. So we follow the has bits with: - // 1. Bools (1 byte) - // 2. Anything always 4 bytes - float, *32, enums - // 3. Anything that is always a pointer (they will be 8 bytes on 64 bit + // 1. Anything always 4 bytes - float, *32, enums + // 2. Anything that is always a pointer (they will be 8 bytes on 64 bit // builds and 4 bytes on 32bit builds. - // 4. Anything always 8 bytes - double, *64 + // 3. Anything always 8 bytes - double, *64 + // + // NOTE: Bools aren't listed, they were stored in the has bits. // // Why? Using 64bit builds as an example, this means worse case, we have // enough bools that we overflow 1 byte from 4 byte alignment, so 3 bytes @@ -115,9 +116,9 @@ int OrderGroupForFieldDescriptor(const FieldDescriptor* descriptor) { case FieldDescriptor::TYPE_ENUM: return 2; - // 1 byte. + // 0 bytes. Stored in the has bits. case FieldDescriptor::TYPE_BOOL: - return 1; + return 99; // End of the list (doesn't really matter). } // Some compilers report reaching end of function even though all cases of @@ -320,8 +321,9 @@ void MessageGenerator::GenerateMessageHeader(io::Printer* printer) { } printer->Print( - "$comments$@interface $classname$ : GPBMessage\n\n", + "$comments$$deprecated_attribute$@interface $classname$ : GPBMessage\n\n", "classname", class_name_, + "deprecated_attribute", GetOptionalDeprecatedAttribute(descriptor_, false, true), "comments", message_comments); vector seen_oneofs(descriptor_->oneof_decl_count(), 0); @@ -404,32 +406,28 @@ void MessageGenerator::GenerateSource(io::Printer* printer) { sort(sorted_extensions.begin(), sorted_extensions.end(), ExtensionRangeOrdering()); - // TODO(thomasvl): Finish optimizing has bit. The current behavior is as - // follows: - // 1. objectivec_field.cc's SetCommonFieldVariables() defaults the has_index - // to the field's index in the list of fields. - // 2. RepeatedFieldGenerator::RepeatedFieldGenerator() sets has_index to - // GPBNoHasBit because repeated fields & map<> fields don't use the has - // bit. - // 3. FieldGenerator::SetOneofIndexBase() overrides has_bit with a negative - // index that groups all the elements on of the oneof. - // So in has_storage, we need enough bits for the single fields that aren't - // in any oneof, and then one int32 for each oneof (to store the field - // number). So we could save a little space by not using the field's index - // and instead make a second pass only assigning indexes for the fields - // that would need it. The only savings would come when messages have over - // a multiple of 32 fields with some number being repeated or in oneofs to - // drop the count below that 32 multiple; so it hasn't seemed worth doing - // at the moment. - size_t num_has_bits = descriptor_->field_count(); + // Assign has bits: + // 1. FieldGeneratorMap::CalculateHasBits() loops through the fields seeing + // who needs has bits and assigning them. + // 2. FieldGenerator::SetOneofIndexBase() overrides has_bit with a negative + // index that groups all the elements in the oneof. + size_t num_has_bits = field_generators_.CalculateHasBits(); size_t sizeof_has_storage = (num_has_bits + 31) / 32; + if (sizeof_has_storage == 0) { + // In the case where no field needs has bits, don't let the _has_storage_ + // end up as zero length (zero length arrays are sort of a grey area + // since it has to be at the start of the struct). This also ensures a + // field with only oneofs keeps the required negative indices they need. + sizeof_has_storage = 1; + } // Tell all the fields the oneof base. for (vector::iterator iter = oneof_generators_.begin(); iter != oneof_generators_.end(); ++iter) { (*iter)->SetOneofIndexBase(sizeof_has_storage); } field_generators_.SetOneofIndexBase(sizeof_has_storage); - // Add an int32 for each oneof to store which is set. + // sizeof_has_storage needs enough bits for the single fields that aren't in + // any oneof, and then one int32 for each oneof (to store the field number). sizeof_has_storage += descriptor_->oneof_decl_count(); printer->Print( @@ -456,47 +454,26 @@ void MessageGenerator::GenerateSource(io::Printer* printer) { " static GPBDescriptor *descriptor = nil;\n" " if (!descriptor) {\n"); - bool has_oneofs = oneof_generators_.size(); - if (has_oneofs) { - printer->Print( - " static GPBMessageOneofDescription oneofs[] = {\n"); - printer->Indent(); - printer->Indent(); - printer->Indent(); - for (vector::iterator iter = oneof_generators_.begin(); - iter != oneof_generators_.end(); ++iter) { - (*iter)->GenerateDescription(printer); - } - printer->Outdent(); - printer->Outdent(); - printer->Outdent(); - printer->Print( - " };\n"); - } - TextFormatDecodeData text_format_decode_data; bool has_fields = descriptor_->field_count() > 0; + bool need_defaults = field_generators_.DoesAnyFieldHaveNonZeroDefault(); + string field_description_type; + if (need_defaults) { + field_description_type = "GPBMessageFieldDescriptionWithDefault"; + } else { + field_description_type = "GPBMessageFieldDescription"; + } if (has_fields) { - // TODO(thomasvl): The plugin's FieldGenerator::GenerateFieldDescription() - // wraps the fieldOptions's value of this structure in an CPP gate so - // they can be compiled away; but that still results in a const char* in - // the structure for a NULL pointer for every message field. If the - // fieldOptions are moved to a separate payload like the TextFormat extra - // data is, then it would shrink that static data shrinking the binaries - // a little more. - // TODO(thomasvl): proto3 syntax doens't need a defaultValue in the - // structure because primitive types are always zero. If we add a second - // structure and a different initializer, we can avoid the wasted static - // storage for every field in a proto3 message. printer->Print( - " static GPBMessageFieldDescription fields[] = {\n"); + " static $field_description_type$ fields[] = {\n", + "field_description_type", field_description_type); printer->Indent(); printer->Indent(); printer->Indent(); for (int i = 0; i < descriptor_->field_count(); ++i) { const FieldGenerator& field_generator = field_generators_.get(sorted_fields[i]); - field_generator.GenerateFieldDescription(printer); + field_generator.GenerateFieldDescription(printer, need_defaults); if (field_generator.needs_textformat_name_support()) { text_format_decode_data.AddString(sorted_fields[i]->number(), field_generator.generated_objc_name(), @@ -510,111 +487,89 @@ void MessageGenerator::GenerateSource(io::Printer* printer) { " };\n"); } - bool has_enums = enum_generators_.size(); - if (has_enums) { - printer->Print( - " static GPBMessageEnumDescription enums[] = {\n"); - printer->Indent(); - printer->Indent(); - printer->Indent(); - for (vector::iterator iter = enum_generators_.begin(); - iter != enum_generators_.end(); ++iter) { - printer->Print("{ .enumDescriptorFunc = $name$_EnumDescriptor },\n", - "name", (*iter)->name()); - } - printer->Outdent(); - printer->Outdent(); - printer->Outdent(); - printer->Print( - " };\n"); - } - - bool has_extensions = sorted_extensions.size(); - if (has_extensions) { - printer->Print( - " static GPBExtensionRange ranges[] = {\n"); - printer->Indent(); - printer->Indent(); - printer->Indent(); - for (int i = 0; i < sorted_extensions.size(); i++) { - printer->Print("{ .start = $start$, .end = $end$ },\n", - "start", SimpleItoa(sorted_extensions[i]->start), - "end", SimpleItoa(sorted_extensions[i]->end)); - } - printer->Outdent(); - printer->Outdent(); - printer->Outdent(); - printer->Print( - " };\n"); - } - map vars; vars["classname"] = class_name_; vars["rootclassname"] = root_classname_; vars["fields"] = has_fields ? "fields" : "NULL"; - vars["fields_count"] = - has_fields ? "sizeof(fields) / sizeof(GPBMessageFieldDescription)" : "0"; - vars["oneofs"] = has_oneofs ? "oneofs" : "NULL"; - vars["oneof_count"] = - has_oneofs ? "sizeof(oneofs) / sizeof(GPBMessageOneofDescription)" : "0"; - vars["enums"] = has_enums ? "enums" : "NULL"; - vars["enum_count"] = - has_enums ? "sizeof(enums) / sizeof(GPBMessageEnumDescription)" : "0"; - vars["ranges"] = has_extensions ? "ranges" : "NULL"; - vars["range_count"] = - has_extensions ? "sizeof(ranges) / sizeof(GPBExtensionRange)" : "0"; - vars["wireformat"] = - descriptor_->options().message_set_wire_format() ? "YES" : "NO"; - - if (text_format_decode_data.num_entries() == 0) { - printer->Print( - vars, - " GPBDescriptor *localDescriptor =\n" - " [GPBDescriptor allocDescriptorForClass:[$classname$ class]\n" - " rootClass:[$rootclassname$ class]\n" - " file:$rootclassname$_FileDescriptor()\n" - " fields:$fields$\n" - " fieldCount:$fields_count$\n" - " oneofs:$oneofs$\n" - " oneofCount:$oneof_count$\n" - " enums:$enums$\n" - " enumCount:$enum_count$\n" - " ranges:$ranges$\n" - " rangeCount:$range_count$\n" - " storageSize:sizeof($classname$__storage_)\n" - " wireFormat:$wireformat$];\n"); + if (has_fields) { + vars["fields_count"] = + "(uint32_t)(sizeof(fields) / sizeof(" + field_description_type + "))"; } else { - vars["extraTextFormatInfo"] = CEscape(text_format_decode_data.Data()); + vars["fields_count"] = "0"; + } + + std::vector init_flags; + if (need_defaults) { + init_flags.push_back("GPBDescriptorInitializationFlag_FieldsWithDefault"); + } + if (descriptor_->options().message_set_wire_format()) { + init_flags.push_back("GPBDescriptorInitializationFlag_WireFormat"); + } + vars["init_flags"] = BuildFlagsString(init_flags); + + printer->Print( + vars, + " GPBDescriptor *localDescriptor =\n" + " [GPBDescriptor allocDescriptorForClass:[$classname$ class]\n" + " rootClass:[$rootclassname$ class]\n" + " file:$rootclassname$_FileDescriptor()\n" + " fields:$fields$\n" + " fieldCount:$fields_count$\n" + " storageSize:sizeof($classname$__storage_)\n" + " flags:$init_flags$];\n"); + if (oneof_generators_.size() != 0) { printer->Print( - vars, - "#if GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS\n" - " const char *extraTextFormatInfo = NULL;\n" - "#else\n" - " static const char *extraTextFormatInfo = \"$extraTextFormatInfo$\";\n" - "#endif // GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS\n" - " GPBDescriptor *localDescriptor =\n" - " [GPBDescriptor allocDescriptorForClass:[$classname$ class]\n" - " rootClass:[$rootclassname$ class]\n" - " file:$rootclassname$_FileDescriptor()\n" - " fields:$fields$\n" - " fieldCount:$fields_count$\n" - " oneofs:$oneofs$\n" - " oneofCount:$oneof_count$\n" - " enums:$enums$\n" - " enumCount:$enum_count$\n" - " ranges:$ranges$\n" - " rangeCount:$range_count$\n" - " storageSize:sizeof($classname$__storage_)\n" - " wireFormat:$wireformat$\n" - " extraTextFormatInfo:extraTextFormatInfo];\n"); + " static const char *oneofs[] = {\n"); + for (vector::iterator iter = oneof_generators_.begin(); + iter != oneof_generators_.end(); ++iter) { + printer->Print( + " \"$name$\",\n", + "name", (*iter)->DescriptorName()); } printer->Print( - " NSAssert(descriptor == nil, @\"Startup recursed!\");\n" - " descriptor = localDescriptor;\n" - " }\n" - " return descriptor;\n" - "}\n\n" - "@end\n\n"); + " };\n" + " [localDescriptor setupOneofs:oneofs\n" + " count:(uint32_t)(sizeof(oneofs) / sizeof(char*))\n" + " firstHasIndex:$first_has_index$];\n", + "first_has_index", oneof_generators_[0]->HasIndexAsString()); + } + if (text_format_decode_data.num_entries() != 0) { + const string text_format_data_str(text_format_decode_data.Data()); + printer->Print( + "#if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS\n" + " static const char *extraTextFormatInfo ="); + static const int kBytesPerLine = 40; // allow for escaping + for (int i = 0; i < text_format_data_str.size(); i += kBytesPerLine) { + printer->Print( + "\n \"$data$\"", + "data", EscapeTrigraphs( + CEscape(text_format_data_str.substr(i, kBytesPerLine)))); + } + printer->Print( + ";\n" + " [localDescriptor setupExtraTextInfo:extraTextFormatInfo];\n" + "#endif // !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS\n"); + } + if (sorted_extensions.size() != 0) { + printer->Print( + " static const GPBExtensionRange ranges[] = {\n"); + for (int i = 0; i < sorted_extensions.size(); i++) { + printer->Print(" { .start = $start$, .end = $end$ },\n", + "start", SimpleItoa(sorted_extensions[i]->start), + "end", SimpleItoa(sorted_extensions[i]->end)); + } + printer->Print( + " };\n" + " [localDescriptor setupExtensionRanges:ranges\n" + " count:(uint32_t)(sizeof(ranges) / sizeof(GPBExtensionRange))];\n"); + } + printer->Print( + " NSAssert(descriptor == nil, @\"Startup recursed!\");\n" + " descriptor = localDescriptor;\n" + " }\n" + " return descriptor;\n" + "}\n\n" + "@end\n\n"); for (int i = 0; i < descriptor_->field_count(); i++) { field_generators_.get(descriptor_->field(i)) diff --git a/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc b/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc index 24e6df076..44bafd7f3 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc @@ -120,17 +120,16 @@ void OneofGenerator::GenerateClearFunctionImplementation(io::Printer* printer) { "void $owning_message_class$_Clear$capitalized_name$OneOfCase($owning_message_class$ *message) {\n" " GPBDescriptor *descriptor = [message descriptor];\n" " GPBOneofDescriptor *oneof = descriptor->oneofs_[$raw_index$];\n" - " GPBMaybeClearOneof(message, oneof, 0);\n" + " GPBMaybeClearOneof(message, oneof, $index$, 0);\n" "}\n"); } -void OneofGenerator::GenerateDescription(io::Printer* printer) { - printer->Print( - variables_, - "{\n" - " .name = \"$name$\",\n" - " .index = $index$,\n" - "},\n"); +string OneofGenerator::DescriptorName(void) const { + return variables_.find("name")->second; +} + +string OneofGenerator::HasIndexAsString(void) const { + return variables_.find("index")->second; } } // namespace objectivec diff --git a/src/google/protobuf/compiler/objectivec/objectivec_oneof.h b/src/google/protobuf/compiler/objectivec/objectivec_oneof.h index bcba82da5..3d9df4dbf 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_oneof.h +++ b/src/google/protobuf/compiler/objectivec/objectivec_oneof.h @@ -61,7 +61,9 @@ class OneofGenerator { void GeneratePropertyImplementation(io::Printer* printer); void GenerateClearFunctionImplementation(io::Printer* printer); - void GenerateDescription(io::Printer* printer); + + string DescriptorName(void) const; + string HasIndexAsString(void) const; private: const OneofDescriptor* descriptor_; diff --git a/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc index ea7f1b918..d49350f4b 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc @@ -134,6 +134,32 @@ PrimitiveFieldGenerator::PrimitiveFieldGenerator( PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {} +void PrimitiveFieldGenerator::GenerateFieldStorageDeclaration( + io::Printer* printer) const { + if (GetObjectiveCType(descriptor_) == OBJECTIVECTYPE_BOOLEAN) { + // Nothing, BOOLs are stored in the has bits. + } else { + SingleFieldGenerator::GenerateFieldStorageDeclaration(printer); + } +} + +int PrimitiveFieldGenerator::ExtraRuntimeHasBitsNeeded(void) const { + if (GetObjectiveCType(descriptor_) == OBJECTIVECTYPE_BOOLEAN) { + // Reserve a bit for the storage of the boolean. + return 1; + } + return 0; +} + +void PrimitiveFieldGenerator::SetExtraRuntimeHasBitsBase(int has_base) { + if (GetObjectiveCType(descriptor_) == OBJECTIVECTYPE_BOOLEAN) { + // Set into the offset the has bit to use for the actual value. + variables_["storage_offset_value"] = SimpleItoa(has_base); + variables_["storage_offset_comment"] = + " // Stored in _has_storage_ to save space."; + } +} + PrimitiveObjFieldGenerator::PrimitiveObjFieldGenerator( const FieldDescriptor* descriptor, const Options& options) : ObjCObjFieldGenerator(descriptor, options) { diff --git a/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.h b/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.h index 87139afb7..69bb1fddc 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.h +++ b/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.h @@ -49,6 +49,11 @@ class PrimitiveFieldGenerator : public SingleFieldGenerator { const Options& options); virtual ~PrimitiveFieldGenerator(); + virtual void GenerateFieldStorageDeclaration(io::Printer* printer) const; + + virtual int ExtraRuntimeHasBitsNeeded(void) const; + virtual void SetExtraRuntimeHasBitsBase(int index_base); + private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator); }; diff --git a/src/google/protobuf/compiler/plugin.cc b/src/google/protobuf/compiler/plugin.cc index 2bebf1f3a..2ff50f61f 100644 --- a/src/google/protobuf/compiler/plugin.cc +++ b/src/google/protobuf/compiler/plugin.cc @@ -93,6 +93,65 @@ class GeneratorResponseContext : public GeneratorContext { const vector& parsed_files_; }; +bool GenerateCode(const CodeGeneratorRequest& request, + const CodeGenerator& generator, CodeGeneratorResponse* response, + string* error_msg) { + DescriptorPool pool; + for (int i = 0; i < request.proto_file_size(); i++) { + const FileDescriptor* file = pool.BuildFile(request.proto_file(i)); + if (file == NULL) { + // BuildFile() already wrote an error message. + return false; + } + } + + vector parsed_files; + for (int i = 0; i < request.file_to_generate_size(); i++) { + parsed_files.push_back(pool.FindFileByName(request.file_to_generate(i))); + if (parsed_files.back() == NULL) { + *error_msg = "protoc asked plugin to generate a file but " + "did not provide a descriptor for the file: " + + request.file_to_generate(i); + return false; + } + } + + GeneratorResponseContext context(response, parsed_files); + + if (generator.HasGenerateAll()) { + string error; + bool succeeded = generator.GenerateAll( + parsed_files, request.parameter(), &context, &error); + + if (!succeeded && error.empty()) { + error = "Code generator returned false but provided no error " + "description."; + } + if (!error.empty()) { + response->set_error(error); + } + } else { + for (int i = 0; i < parsed_files.size(); i++) { + const FileDescriptor* file = parsed_files[i]; + + string error; + bool succeeded = generator.Generate( + file, request.parameter(), &context, &error); + + if (!succeeded && error.empty()) { + error = "Code generator returned false but provided no error " + "description."; + } + if (!error.empty()) { + response->set_error(file->name() + ": " + error); + break; + } + } + } + + return true; +} + int PluginMain(int argc, char* argv[], const CodeGenerator* generator) { if (argc > 1) { @@ -112,62 +171,18 @@ int PluginMain(int argc, char* argv[], const CodeGenerator* generator) { return 1; } - DescriptorPool pool; - for (int i = 0; i < request.proto_file_size(); i++) { - const FileDescriptor* file = pool.BuildFile(request.proto_file(i)); - if (file == NULL) { - // BuildFile() already wrote an error message. - return 1; - } - } - - vector parsed_files; - for (int i = 0; i < request.file_to_generate_size(); i++) { - parsed_files.push_back(pool.FindFileByName(request.file_to_generate(i))); - if (parsed_files.back() == NULL) { - std::cerr << argv[0] << ": protoc asked plugin to generate a file but " - "did not provide a descriptor for the file: " - << request.file_to_generate(i) << std::endl; - return 1; - } - } - + string error_msg; CodeGeneratorResponse response; - GeneratorResponseContext context(&response, parsed_files); - if (generator->HasGenerateAll()) { - string error; - bool succeeded = generator->GenerateAll( - parsed_files, request.parameter(), &context, &error); - - if (!succeeded && error.empty()) { - error = "Code generator returned false but provided no error " - "description."; - } - if (!error.empty()) { - response.set_error(error); + if (GenerateCode(request, *generator, &response, &error_msg)) { + if (!response.SerializeToFileDescriptor(STDOUT_FILENO)) { + std::cerr << argv[0] << ": Error writing to stdout." << std::endl; + return 1; } } else { - for (int i = 0; i < parsed_files.size(); i++) { - const FileDescriptor* file = parsed_files[i]; - - string error; - bool succeeded = generator->Generate( - file, request.parameter(), &context, &error); - - if (!succeeded && error.empty()) { - error = "Code generator returned false but provided no error " - "description."; - } - if (!error.empty()) { - response.set_error(file->name() + ": " + error); - break; - } + if (!error_msg.empty()) { + std::cerr << argv[0] << ": " << error_msg << std::endl; } - } - - if (!response.SerializeToFileDescriptor(STDOUT_FILENO)) { - std::cerr << argv[0] << ": Error writing to stdout." << std::endl; return 1; } diff --git a/src/google/protobuf/compiler/plugin.h b/src/google/protobuf/compiler/plugin.h index 679f9bdb8..d2793a9ff 100644 --- a/src/google/protobuf/compiler/plugin.h +++ b/src/google/protobuf/compiler/plugin.h @@ -40,6 +40,13 @@ // } // You must link your plugin against libprotobuf and libprotoc. // +// The core part of PluginMain is to invoke the given CodeGenerator on a +// CodeGeneratorRequest to generate a CodeGeneratorResponse. This part is +// abstracted out and made into function GenerateCode so that it can be reused, +// for example, to implement a variant of PluginMain that does some +// preprocessing on the input CodeGeneratorRequest before feeding the request +// to the given code generator. +// // To get protoc to use the plugin, do one of the following: // * Place the plugin binary somewhere in the PATH and give it the name // "protoc-gen-NAME" (replacing "NAME" with the name of your plugin). If you @@ -55,16 +62,27 @@ #ifndef GOOGLE_PROTOBUF_COMPILER_PLUGIN_H__ #define GOOGLE_PROTOBUF_COMPILER_PLUGIN_H__ +#include + #include namespace google { namespace protobuf { namespace compiler { class CodeGenerator; // code_generator.h +class CodeGeneratorRequest; +class CodeGeneratorResponse; // Implements main() for a protoc plugin exposing the given code generator. LIBPROTOC_EXPORT int PluginMain(int argc, char* argv[], const CodeGenerator* generator); +// Generates code using the given code generator. Returns true if the code +// generation is successful. If the code geneartion fails, error_msg may be +// populated to describe the failure cause. +bool GenerateCode(const CodeGeneratorRequest& request, + const CodeGenerator& generator, CodeGeneratorResponse* response, + string* error_msg); + } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/plugin.pb.cc b/src/google/protobuf/compiler/plugin.pb.cc index a2da8eee3..e9d50a1dc 100644 --- a/src/google/protobuf/compiler/plugin.pb.cc +++ b/src/google/protobuf/compiler/plugin.pb.cc @@ -240,6 +240,7 @@ CodeGeneratorRequest* CodeGeneratorRequest::New(::google::protobuf::Arena* arena } void CodeGeneratorRequest::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.compiler.CodeGeneratorRequest) if (has_parameter()) { parameter_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -253,7 +254,7 @@ void CodeGeneratorRequest::Clear() { bool CodeGeneratorRequest::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.compiler.CodeGeneratorRequest) for (;;) { @@ -412,6 +413,7 @@ void CodeGeneratorRequest::SerializeWithCachedSizes( } int CodeGeneratorRequest::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.compiler.CodeGeneratorRequest) int total_size = 0; // optional string parameter = 2; @@ -448,18 +450,22 @@ int CodeGeneratorRequest::ByteSize() const { } void CodeGeneratorRequest::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.compiler.CodeGeneratorRequest) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const CodeGeneratorRequest* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.compiler.CodeGeneratorRequest) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.compiler.CodeGeneratorRequest) MergeFrom(*source); } } void CodeGeneratorRequest::MergeFrom(const CodeGeneratorRequest& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.compiler.CodeGeneratorRequest) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); file_to_generate_.MergeFrom(from.file_to_generate_); proto_file_.MergeFrom(from.proto_file_); @@ -475,12 +481,14 @@ void CodeGeneratorRequest::MergeFrom(const CodeGeneratorRequest& from) { } void CodeGeneratorRequest::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.compiler.CodeGeneratorRequest) if (&from == this) return; Clear(); MergeFrom(from); } void CodeGeneratorRequest::CopyFrom(const CodeGeneratorRequest& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.compiler.CodeGeneratorRequest) if (&from == this) return; Clear(); MergeFrom(from); @@ -545,6 +553,7 @@ void CodeGeneratorRequest::clear_file_to_generate() { // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) } ::std::string* CodeGeneratorRequest::add_file_to_generate() { + // @@protoc_insertion_point(field_add_mutable:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) return file_to_generate_.Add(); } void CodeGeneratorRequest::add_file_to_generate(const ::std::string& value) { @@ -610,6 +619,7 @@ void CodeGeneratorRequest::clear_parameter() { return parameter_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* CodeGeneratorRequest::release_parameter() { + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorRequest.parameter) clear_has_parameter(); return parameter_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -728,6 +738,7 @@ CodeGeneratorResponse_File* CodeGeneratorResponse_File::New(::google::protobuf:: } void CodeGeneratorResponse_File::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.compiler.CodeGeneratorResponse.File) if (_has_bits_[0 / 32] & 7u) { if (has_name()) { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); @@ -747,7 +758,7 @@ void CodeGeneratorResponse_File::Clear() { bool CodeGeneratorResponse_File::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.compiler.CodeGeneratorResponse.File) for (;;) { @@ -912,6 +923,7 @@ void CodeGeneratorResponse_File::SerializeWithCachedSizes( } int CodeGeneratorResponse_File::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.compiler.CodeGeneratorResponse.File) int total_size = 0; if (_has_bits_[0 / 32] & 7u) { @@ -949,18 +961,22 @@ int CodeGeneratorResponse_File::ByteSize() const { } void CodeGeneratorResponse_File::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.compiler.CodeGeneratorResponse.File) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const CodeGeneratorResponse_File* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.compiler.CodeGeneratorResponse.File) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.compiler.CodeGeneratorResponse.File) MergeFrom(*source); } } void CodeGeneratorResponse_File::MergeFrom(const CodeGeneratorResponse_File& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.compiler.CodeGeneratorResponse.File) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { if (from.has_name()) { @@ -982,12 +998,14 @@ void CodeGeneratorResponse_File::MergeFrom(const CodeGeneratorResponse_File& fro } void CodeGeneratorResponse_File::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.compiler.CodeGeneratorResponse.File) if (&from == this) return; Clear(); MergeFrom(from); } void CodeGeneratorResponse_File::CopyFrom(const CodeGeneratorResponse_File& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.compiler.CodeGeneratorResponse.File) if (&from == this) return; Clear(); MergeFrom(from); @@ -1088,6 +1106,7 @@ CodeGeneratorResponse* CodeGeneratorResponse::New(::google::protobuf::Arena* are } void CodeGeneratorResponse::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.compiler.CodeGeneratorResponse) if (has_error()) { error_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1100,7 +1119,7 @@ void CodeGeneratorResponse::Clear() { bool CodeGeneratorResponse::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.compiler.CodeGeneratorResponse) for (;;) { @@ -1219,6 +1238,7 @@ void CodeGeneratorResponse::SerializeWithCachedSizes( } int CodeGeneratorResponse::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.compiler.CodeGeneratorResponse) int total_size = 0; // optional string error = 1; @@ -1248,18 +1268,22 @@ int CodeGeneratorResponse::ByteSize() const { } void CodeGeneratorResponse::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.compiler.CodeGeneratorResponse) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const CodeGeneratorResponse* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.compiler.CodeGeneratorResponse) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.compiler.CodeGeneratorResponse) MergeFrom(*source); } } void CodeGeneratorResponse::MergeFrom(const CodeGeneratorResponse& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.compiler.CodeGeneratorResponse) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); file_.MergeFrom(from.file_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { @@ -1274,12 +1298,14 @@ void CodeGeneratorResponse::MergeFrom(const CodeGeneratorResponse& from) { } void CodeGeneratorResponse::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.compiler.CodeGeneratorResponse) if (&from == this) return; Clear(); MergeFrom(from); } void CodeGeneratorResponse::CopyFrom(const CodeGeneratorResponse& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.compiler.CodeGeneratorResponse) if (&from == this) return; Clear(); MergeFrom(from); @@ -1353,6 +1379,7 @@ void CodeGeneratorResponse_File::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* CodeGeneratorResponse_File::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorResponse.File.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1406,6 +1433,7 @@ void CodeGeneratorResponse_File::clear_insertion_point() { return insertion_point_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* CodeGeneratorResponse_File::release_insertion_point() { + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point) clear_has_insertion_point(); return insertion_point_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1459,6 +1487,7 @@ void CodeGeneratorResponse_File::clear_content() { return content_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* CodeGeneratorResponse_File::release_content() { + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorResponse.File.content) clear_has_content(); return content_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1516,6 +1545,7 @@ void CodeGeneratorResponse::clear_error() { return error_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* CodeGeneratorResponse::release_error() { + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorResponse.error) clear_has_error(); return error_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } diff --git a/src/google/protobuf/compiler/plugin.pb.h b/src/google/protobuf/compiler/plugin.pb.h index 0a03e9791..510202f0a 100644 --- a/src/google/protobuf/compiler/plugin.pb.h +++ b/src/google/protobuf/compiler/plugin.pb.h @@ -437,6 +437,7 @@ inline void CodeGeneratorRequest::set_file_to_generate(int index, const char* va // @@protoc_insertion_point(field_set_pointer:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) } inline ::std::string* CodeGeneratorRequest::add_file_to_generate() { + // @@protoc_insertion_point(field_add_mutable:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) return file_to_generate_.Add(); } inline void CodeGeneratorRequest::add_file_to_generate(const ::std::string& value) { @@ -502,6 +503,7 @@ inline ::std::string* CodeGeneratorRequest::mutable_parameter() { return parameter_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* CodeGeneratorRequest::release_parameter() { + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorRequest.parameter) clear_has_parameter(); return parameter_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -589,6 +591,7 @@ inline ::std::string* CodeGeneratorResponse_File::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* CodeGeneratorResponse_File::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorResponse.File.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -642,6 +645,7 @@ inline ::std::string* CodeGeneratorResponse_File::mutable_insertion_point() { return insertion_point_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* CodeGeneratorResponse_File::release_insertion_point() { + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point) clear_has_insertion_point(); return insertion_point_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -695,6 +699,7 @@ inline ::std::string* CodeGeneratorResponse_File::mutable_content() { return content_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* CodeGeneratorResponse_File::release_content() { + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorResponse.File.content) clear_has_content(); return content_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -752,6 +757,7 @@ inline ::std::string* CodeGeneratorResponse::mutable_error() { return error_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* CodeGeneratorResponse::release_error() { + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorResponse.error) clear_has_error(); return error_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } diff --git a/src/google/protobuf/compiler/python/python_generator.cc b/src/google/protobuf/compiler/python/python_generator.cc index 4d500f90e..0553dd0d9 100644 --- a/src/google/protobuf/compiler/python/python_generator.cc +++ b/src/google/protobuf/compiler/python/python_generator.cc @@ -235,7 +235,7 @@ string StringifyDefaultValue(const FieldDescriptor& field) { // infinity * 0 = nan return "(1e10000 * 0)"; } else { - return SimpleDtoa(value); + return "float(" + SimpleDtoa(value) + ")"; } } case FieldDescriptor::CPPTYPE_FLOAT: { @@ -251,7 +251,7 @@ string StringifyDefaultValue(const FieldDescriptor& field) { // infinity - infinity = nan return "(1e10000 * 0)"; } else { - return SimpleFtoa(value); + return "float(" + SimpleFtoa(value) + ")"; } } case FieldDescriptor::CPPTYPE_BOOL: diff --git a/src/google/protobuf/compiler/python/python_plugin_unittest.cc b/src/google/protobuf/compiler/python/python_plugin_unittest.cc index e82bbae7b..23f2449cd 100644 --- a/src/google/protobuf/compiler/python/python_plugin_unittest.cc +++ b/src/google/protobuf/compiler/python/python_plugin_unittest.cc @@ -44,9 +44,10 @@ #include #include +#include +#include #include #include -#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/compiler/subprocess.cc b/src/google/protobuf/compiler/subprocess.cc index a30ac3051..6e2586641 100644 --- a/src/google/protobuf/compiler/subprocess.cc +++ b/src/google/protobuf/compiler/subprocess.cc @@ -361,7 +361,7 @@ bool Subprocess::Communicate(const Message& input, Message* output, string output_data; int input_pos = 0; - int max_fd = max(child_stdin_, child_stdout_); + int max_fd = std::max(child_stdin_, child_stdout_); while (child_stdout_ != -1) { fd_set read_fds; diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc index 78a34617f..56e11fa96 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -357,13 +357,20 @@ void DeleteAllowedProto3Extendee() { void InitAllowedProto3Extendee() { allowed_proto3_extendees_ = new set; - allowed_proto3_extendees_->insert("google.protobuf.FileOptions"); - allowed_proto3_extendees_->insert("google.protobuf.MessageOptions"); - allowed_proto3_extendees_->insert("google.protobuf.FieldOptions"); - allowed_proto3_extendees_->insert("google.protobuf.EnumOptions"); - allowed_proto3_extendees_->insert("google.protobuf.EnumValueOptions"); - allowed_proto3_extendees_->insert("google.protobuf.ServiceOptions"); - allowed_proto3_extendees_->insert("google.protobuf.MethodOptions"); + const char* kOptionNames[] = { + "FileOptions", "MessageOptions", "FieldOptions", "EnumOptions", + "EnumValueOptions", "ServiceOptions", "MethodOptions"}; + for (int i = 0; i < GOOGLE_ARRAYSIZE(kOptionNames); ++i) { + // descriptor.proto has a different package name in opensource. We allow + // both so the opensource protocol compiler can also compile internal + // proto3 files with custom options. See: b/27567912 + allowed_proto3_extendees_->insert(string("google.protobuf.") + + kOptionNames[i]); + // Split the word to trick the opensource processing scripts so they + // will keep the origial package name. + allowed_proto3_extendees_->insert(string("proto") + "2." + kOptionNames[i]); + } + google::protobuf::internal::OnShutdown(&DeleteAllowedProto3Extendee); } @@ -2766,6 +2773,9 @@ class DescriptorBuilder { private: friend class OptionInterpreter; + // Non-recursive part of BuildFile functionality. + const FileDescriptor* BuildFileImpl(const FileDescriptorProto& proto); + const DescriptorPool* pool_; DescriptorPool::Tables* tables_; // for convenience DescriptorPool::ErrorCollector* error_collector_; @@ -3834,7 +3844,11 @@ const FileDescriptor* DescriptorBuilder::BuildFile( } tables_->pending_files_.pop_back(); } + return BuildFileImpl(proto); +} +const FileDescriptor* DescriptorBuilder::BuildFileImpl( + const FileDescriptorProto& proto) { // Checkpoint the tables so that we can roll back if something goes wrong. tables_->AddCheckpoint(); @@ -5113,11 +5127,6 @@ void DescriptorBuilder::ValidateProto3( for (int i = 0; i < file->enum_type_count(); ++i) { ValidateProto3Enum(file->enum_types_ + i, proto.enum_type(i)); } - if (IsLite(file)) { - AddError(file->name(), proto, - DescriptorPool::ErrorCollector::OTHER, - "Lite runtime is not supported in proto3."); - } } static string ToLowercaseWithoutUnderscores(const string& name) { diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h index 7e3a74966..3ecc0a9c0 100644 --- a/src/google/protobuf/descriptor.h +++ b/src/google/protobuf/descriptor.h @@ -127,6 +127,11 @@ namespace descriptor_unittest { class DescriptorTest; } // namespace descriptor_unittest +// Defined in printer.h +namespace io { +class Printer; +} // namespace io + // NB, all indices are zero-based. struct SourceLocation { int start_line; @@ -359,6 +364,9 @@ class LIBPROTOBUF_EXPORT Descriptor { // Allows tests to test CopyTo(proto, true). friend class ::google::protobuf::descriptor_unittest::DescriptorTest; + // Allows access to GetLocationPath for annotations. + friend class ::google::protobuf::io::Printer; + // Fill the json_name field of FieldDescriptorProto. void CopyJsonNameTo(DescriptorProto* proto) const; @@ -644,6 +652,9 @@ class LIBPROTOBUF_EXPORT FieldDescriptor { private: typedef FieldOptions OptionsType; + // Allows access to GetLocationPath for annotations. + friend class ::google::protobuf::io::Printer; + // Fill the json_name field of FieldDescriptorProto. void CopyJsonNameTo(FieldDescriptorProto* proto) const; @@ -756,6 +767,9 @@ class LIBPROTOBUF_EXPORT OneofDescriptor { bool GetSourceLocation(SourceLocation* out_location) const; private: + // Allows access to GetLocationPath for annotations. + friend class ::google::protobuf::io::Printer; + // See Descriptor::DebugString(). void DebugString(int depth, string* contents, const DebugStringOptions& options) const; @@ -846,6 +860,9 @@ class LIBPROTOBUF_EXPORT EnumDescriptor { private: typedef EnumOptions OptionsType; + // Allows access to GetLocationPath for annotations. + friend class ::google::protobuf::io::Printer; + // Looks up a value by number. If the value does not exist, dynamically // creates a new EnumValueDescriptor for that value, assuming that it was // unknown. If a new descriptor is created, this is done in a thread-safe way, @@ -942,6 +959,9 @@ class LIBPROTOBUF_EXPORT EnumValueDescriptor { private: typedef EnumValueOptions OptionsType; + // Allows access to GetLocationPath for annotations. + friend class ::google::protobuf::io::Printer; + // See Descriptor::DebugString(). void DebugString(int depth, string *contents, const DebugStringOptions& options) const; @@ -1018,6 +1038,9 @@ class LIBPROTOBUF_EXPORT ServiceDescriptor { private: typedef ServiceOptions OptionsType; + // Allows access to GetLocationPath for annotations. + friend class ::google::protobuf::io::Printer; + // See Descriptor::DebugString(). void DebugString(string *contents, const DebugStringOptions& options) const; @@ -1096,6 +1119,9 @@ class LIBPROTOBUF_EXPORT MethodDescriptor { private: typedef MethodOptions OptionsType; + // Allows access to GetLocationPath for annotations. + friend class ::google::protobuf::io::Printer; + // See Descriptor::DebugString(). void DebugString(int depth, string *contents, const DebugStringOptions& options) const; diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc index ff0cfcf8c..4d5c4d99d 100644 --- a/src/google/protobuf/descriptor.pb.cc +++ b/src/google/protobuf/descriptor.pb.cc @@ -319,7 +319,7 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, _internal_metadata_), -1); FileOptions_descriptor_ = file->message_type(9); - static const int FileOptions_offsets_[16] = { + static const int FileOptions_offsets_[15] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_package_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_outer_classname_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_multiple_files_), @@ -334,7 +334,6 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, cc_enable_arenas_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, objc_class_prefix_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, csharp_namespace_), - GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, javanano_use_deprecated_package_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, uninterpreted_option_), }; FileOptions_reflection_ = @@ -746,7 +745,7 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { "_type\030\003 \001(\t\022/\n\007options\030\004 \001(\0132\036.google.pr" "otobuf.MethodOptions\022\037\n\020client_streaming" "\030\005 \001(\010:\005false\022\037\n\020server_streaming\030\006 \001(\010:" - "\005false\"\256\005\n\013FileOptions\022\024\n\014java_package\030\001" + "\005false\"\207\005\n\013FileOptions\022\024\n\014java_package\030\001" " \001(\t\022\034\n\024java_outer_classname\030\010 \001(\t\022\"\n\023ja" "va_multiple_files\030\n \001(\010:\005false\022,\n\035java_g" "enerate_equals_and_hash\030\024 \001(\010:\005false\022%\n\026" @@ -758,60 +757,59 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { "e\022\"\n\023py_generic_services\030\022 \001(\010:\005false\022\031\n" "\ndeprecated\030\027 \001(\010:\005false\022\037\n\020cc_enable_ar" "enas\030\037 \001(\010:\005false\022\031\n\021objc_class_prefix\030$" - " \001(\t\022\030\n\020csharp_namespace\030% \001(\t\022+\n\037javana" - "no_use_deprecated_package\030& \001(\010B\002\030\001\022C\n\024u" - "ninterpreted_option\030\347\007 \003(\0132$.google.prot" - "obuf.UninterpretedOption\":\n\014OptimizeMode" - "\022\t\n\005SPEED\020\001\022\r\n\tCODE_SIZE\020\002\022\020\n\014LITE_RUNTI" - "ME\020\003*\t\010\350\007\020\200\200\200\200\002\"\346\001\n\016MessageOptions\022&\n\027me" - "ssage_set_wire_format\030\001 \001(\010:\005false\022.\n\037no" - "_standard_descriptor_accessor\030\002 \001(\010:\005fal" - "se\022\031\n\ndeprecated\030\003 \001(\010:\005false\022\021\n\tmap_ent" - "ry\030\007 \001(\010\022C\n\024uninterpreted_option\030\347\007 \003(\0132" - "$.google.protobuf.UninterpretedOption*\t\010" - "\350\007\020\200\200\200\200\002\"\230\003\n\014FieldOptions\022:\n\005ctype\030\001 \001(\016" - "2#.google.protobuf.FieldOptions.CType:\006S" - "TRING\022\016\n\006packed\030\002 \001(\010\022\?\n\006jstype\030\006 \001(\0162$." - "google.protobuf.FieldOptions.JSType:\tJS_" - "NORMAL\022\023\n\004lazy\030\005 \001(\010:\005false\022\031\n\ndeprecate" - "d\030\003 \001(\010:\005false\022\023\n\004weak\030\n \001(\010:\005false\022C\n\024u" - "ninterpreted_option\030\347\007 \003(\0132$.google.prot" - "obuf.UninterpretedOption\"/\n\005CType\022\n\n\006STR" - "ING\020\000\022\010\n\004CORD\020\001\022\020\n\014STRING_PIECE\020\002\"5\n\006JST" - "ype\022\r\n\tJS_NORMAL\020\000\022\r\n\tJS_STRING\020\001\022\r\n\tJS_" - "NUMBER\020\002*\t\010\350\007\020\200\200\200\200\002\"\215\001\n\013EnumOptions\022\023\n\013a" - "llow_alias\030\002 \001(\010\022\031\n\ndeprecated\030\003 \001(\010:\005fa" - "lse\022C\n\024uninterpreted_option\030\347\007 \003(\0132$.goo" - "gle.protobuf.UninterpretedOption*\t\010\350\007\020\200\200" - "\200\200\002\"}\n\020EnumValueOptions\022\031\n\ndeprecated\030\001 " - "\001(\010:\005false\022C\n\024uninterpreted_option\030\347\007 \003(" - "\0132$.google.protobuf.UninterpretedOption*" - "\t\010\350\007\020\200\200\200\200\002\"{\n\016ServiceOptions\022\031\n\ndeprecat" - "ed\030! \001(\010:\005false\022C\n\024uninterpreted_option\030" - "\347\007 \003(\0132$.google.protobuf.UninterpretedOp" - "tion*\t\010\350\007\020\200\200\200\200\002\"z\n\rMethodOptions\022\031\n\ndepr" - "ecated\030! \001(\010:\005false\022C\n\024uninterpreted_opt" - "ion\030\347\007 \003(\0132$.google.protobuf.Uninterpret" - "edOption*\t\010\350\007\020\200\200\200\200\002\"\236\002\n\023UninterpretedOpt" - "ion\022;\n\004name\030\002 \003(\0132-.google.protobuf.Unin" - "terpretedOption.NamePart\022\030\n\020identifier_v" - "alue\030\003 \001(\t\022\032\n\022positive_int_value\030\004 \001(\004\022\032" - "\n\022negative_int_value\030\005 \001(\003\022\024\n\014double_val" - "ue\030\006 \001(\001\022\024\n\014string_value\030\007 \001(\014\022\027\n\017aggreg" - "ate_value\030\010 \001(\t\0323\n\010NamePart\022\021\n\tname_part" - "\030\001 \002(\t\022\024\n\014is_extension\030\002 \002(\010\"\325\001\n\016SourceC" - "odeInfo\022:\n\010location\030\001 \003(\0132(.google.proto" - "buf.SourceCodeInfo.Location\032\206\001\n\010Location" - "\022\020\n\004path\030\001 \003(\005B\002\020\001\022\020\n\004span\030\002 \003(\005B\002\020\001\022\030\n\020" - "leading_comments\030\003 \001(\t\022\031\n\021trailing_comme" - "nts\030\004 \001(\t\022!\n\031leading_detached_comments\030\006" - " \003(\t\"\247\001\n\021GeneratedCodeInfo\022A\n\nannotation" - "\030\001 \003(\0132-.google.protobuf.GeneratedCodeIn" - "fo.Annotation\032O\n\nAnnotation\022\020\n\004path\030\001 \003(" - "\005B\002\020\001\022\023\n\013source_file\030\002 \001(\t\022\r\n\005begin\030\003 \001(" - "\005\022\013\n\003end\030\004 \001(\005BX\n\023com.google.protobufB\020D" - "escriptorProtosH\001Z\ndescriptor\242\002\003GPB\252\002\032Go" - "ogle.Protobuf.Reflection", 5184); + " \001(\t\022\030\n\020csharp_namespace\030% \001(\t\022C\n\024uninte" + "rpreted_option\030\347\007 \003(\0132$.google.protobuf." + "UninterpretedOption\":\n\014OptimizeMode\022\t\n\005S" + "PEED\020\001\022\r\n\tCODE_SIZE\020\002\022\020\n\014LITE_RUNTIME\020\003*" + "\t\010\350\007\020\200\200\200\200\002J\004\010&\020\'\"\346\001\n\016MessageOptions\022&\n\027m" + "essage_set_wire_format\030\001 \001(\010:\005false\022.\n\037n" + "o_standard_descriptor_accessor\030\002 \001(\010:\005fa" + "lse\022\031\n\ndeprecated\030\003 \001(\010:\005false\022\021\n\tmap_en" + "try\030\007 \001(\010\022C\n\024uninterpreted_option\030\347\007 \003(\013" + "2$.google.protobuf.UninterpretedOption*\t" + "\010\350\007\020\200\200\200\200\002\"\230\003\n\014FieldOptions\022:\n\005ctype\030\001 \001(" + "\0162#.google.protobuf.FieldOptions.CType:\006" + "STRING\022\016\n\006packed\030\002 \001(\010\022\?\n\006jstype\030\006 \001(\0162$" + ".google.protobuf.FieldOptions.JSType:\tJS" + "_NORMAL\022\023\n\004lazy\030\005 \001(\010:\005false\022\031\n\ndeprecat" + "ed\030\003 \001(\010:\005false\022\023\n\004weak\030\n \001(\010:\005false\022C\n\024" + "uninterpreted_option\030\347\007 \003(\0132$.google.pro" + "tobuf.UninterpretedOption\"/\n\005CType\022\n\n\006ST" + "RING\020\000\022\010\n\004CORD\020\001\022\020\n\014STRING_PIECE\020\002\"5\n\006JS" + "Type\022\r\n\tJS_NORMAL\020\000\022\r\n\tJS_STRING\020\001\022\r\n\tJS" + "_NUMBER\020\002*\t\010\350\007\020\200\200\200\200\002\"\215\001\n\013EnumOptions\022\023\n\013" + "allow_alias\030\002 \001(\010\022\031\n\ndeprecated\030\003 \001(\010:\005f" + "alse\022C\n\024uninterpreted_option\030\347\007 \003(\0132$.go" + "ogle.protobuf.UninterpretedOption*\t\010\350\007\020\200" + "\200\200\200\002\"}\n\020EnumValueOptions\022\031\n\ndeprecated\030\001" + " \001(\010:\005false\022C\n\024uninterpreted_option\030\347\007 \003" + "(\0132$.google.protobuf.UninterpretedOption" + "*\t\010\350\007\020\200\200\200\200\002\"{\n\016ServiceOptions\022\031\n\ndepreca" + "ted\030! \001(\010:\005false\022C\n\024uninterpreted_option" + "\030\347\007 \003(\0132$.google.protobuf.UninterpretedO" + "ption*\t\010\350\007\020\200\200\200\200\002\"z\n\rMethodOptions\022\031\n\ndep" + "recated\030! \001(\010:\005false\022C\n\024uninterpreted_op" + "tion\030\347\007 \003(\0132$.google.protobuf.Uninterpre" + "tedOption*\t\010\350\007\020\200\200\200\200\002\"\236\002\n\023UninterpretedOp" + "tion\022;\n\004name\030\002 \003(\0132-.google.protobuf.Uni" + "nterpretedOption.NamePart\022\030\n\020identifier_" + "value\030\003 \001(\t\022\032\n\022positive_int_value\030\004 \001(\004\022" + "\032\n\022negative_int_value\030\005 \001(\003\022\024\n\014double_va" + "lue\030\006 \001(\001\022\024\n\014string_value\030\007 \001(\014\022\027\n\017aggre" + "gate_value\030\010 \001(\t\0323\n\010NamePart\022\021\n\tname_par" + "t\030\001 \002(\t\022\024\n\014is_extension\030\002 \002(\010\"\325\001\n\016Source" + "CodeInfo\022:\n\010location\030\001 \003(\0132(.google.prot" + "obuf.SourceCodeInfo.Location\032\206\001\n\010Locatio" + "n\022\020\n\004path\030\001 \003(\005B\002\020\001\022\020\n\004span\030\002 \003(\005B\002\020\001\022\030\n" + "\020leading_comments\030\003 \001(\t\022\031\n\021trailing_comm" + "ents\030\004 \001(\t\022!\n\031leading_detached_comments\030" + "\006 \003(\t\"\247\001\n\021GeneratedCodeInfo\022A\n\nannotatio" + "n\030\001 \003(\0132-.google.protobuf.GeneratedCodeI" + "nfo.Annotation\032O\n\nAnnotation\022\020\n\004path\030\001 \003" + "(\005B\002\020\001\022\023\n\013source_file\030\002 \001(\t\022\r\n\005begin\030\003 \001" + "(\005\022\013\n\003end\030\004 \001(\005BX\n\023com.google.protobufB\020" + "DescriptorProtosH\001Z\ndescriptor\242\002\003GPB\252\002\032G" + "oogle.Protobuf.Reflection", 5145); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/descriptor.proto", &protobuf_RegisterTypes); FileDescriptorSet::default_instance_ = new FileDescriptorSet(); @@ -946,6 +944,7 @@ FileDescriptorSet* FileDescriptorSet::New(::google::protobuf::Arena* arena) cons } void FileDescriptorSet::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.FileDescriptorSet) file_.Clear(); ::memset(_has_bits_, 0, sizeof(_has_bits_)); if (_internal_metadata_.have_unknown_fields()) { @@ -955,7 +954,7 @@ void FileDescriptorSet::Clear() { bool FileDescriptorSet::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.FileDescriptorSet) for (;;) { @@ -1036,6 +1035,7 @@ void FileDescriptorSet::SerializeWithCachedSizes( } int FileDescriptorSet::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FileDescriptorSet) int total_size = 0; // repeated .google.protobuf.FileDescriptorProto file = 1; @@ -1058,18 +1058,22 @@ int FileDescriptorSet::ByteSize() const { } void FileDescriptorSet::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.FileDescriptorSet) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const FileDescriptorSet* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.FileDescriptorSet) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.FileDescriptorSet) MergeFrom(*source); } } void FileDescriptorSet::MergeFrom(const FileDescriptorSet& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FileDescriptorSet) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); file_.MergeFrom(from.file_); if (from._internal_metadata_.have_unknown_fields()) { @@ -1078,12 +1082,14 @@ void FileDescriptorSet::MergeFrom(const FileDescriptorSet& from) { } void FileDescriptorSet::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.FileDescriptorSet) if (&from == this) return; Clear(); MergeFrom(from); } void FileDescriptorSet::CopyFrom(const FileDescriptorSet& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FileDescriptorSet) if (&from == this) return; Clear(); MergeFrom(from); @@ -1237,6 +1243,7 @@ FileDescriptorProto* FileDescriptorProto::New(::google::protobuf::Arena* arena) } void FileDescriptorProto::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.FileDescriptorProto) if (_has_bits_[0 / 32] & 3u) { if (has_name()) { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); @@ -1271,7 +1278,7 @@ void FileDescriptorProto::Clear() { bool FileDescriptorProto::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.FileDescriptorProto) for (;;) { @@ -1706,6 +1713,7 @@ void FileDescriptorProto::SerializeWithCachedSizes( } int FileDescriptorProto::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FileDescriptorProto) int total_size = 0; if (_has_bits_[0 / 32] & 3u) { @@ -1818,18 +1826,22 @@ int FileDescriptorProto::ByteSize() const { } void FileDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.FileDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const FileDescriptorProto* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.FileDescriptorProto) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.FileDescriptorProto) MergeFrom(*source); } } void FileDescriptorProto::MergeFrom(const FileDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FileDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); dependency_.MergeFrom(from.dependency_); public_dependency_.MergeFrom(from.public_dependency_); @@ -1866,12 +1878,14 @@ void FileDescriptorProto::MergeFrom(const FileDescriptorProto& from) { } void FileDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.FileDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); } void FileDescriptorProto::CopyFrom(const FileDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FileDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); @@ -1962,6 +1976,7 @@ void FileDescriptorProto::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* FileDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -2015,6 +2030,7 @@ void FileDescriptorProto::clear_package() { return package_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* FileDescriptorProto::release_package() { + // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.package) clear_has_package(); return package_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -2057,6 +2073,7 @@ void FileDescriptorProto::clear_dependency() { // @@protoc_insertion_point(field_set_pointer:google.protobuf.FileDescriptorProto.dependency) } ::std::string* FileDescriptorProto::add_dependency() { + // @@protoc_insertion_point(field_add_mutable:google.protobuf.FileDescriptorProto.dependency) return dependency_.Add(); } void FileDescriptorProto::add_dependency(const ::std::string& value) { @@ -2289,6 +2306,7 @@ const ::google::protobuf::FileOptions& FileDescriptorProto::options() const { return options_; } ::google::protobuf::FileOptions* FileDescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.options) clear_has_options(); ::google::protobuf::FileOptions* temp = options_; options_ = NULL; @@ -2332,6 +2350,7 @@ const ::google::protobuf::SourceCodeInfo& FileDescriptorProto::source_code_info( return source_code_info_; } ::google::protobuf::SourceCodeInfo* FileDescriptorProto::release_source_code_info() { + // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.source_code_info) clear_has_source_code_info(); ::google::protobuf::SourceCodeInfo* temp = source_code_info_; source_code_info_ = NULL; @@ -2388,6 +2407,7 @@ void FileDescriptorProto::clear_syntax() { return syntax_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* FileDescriptorProto::release_syntax() { + // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.syntax) clear_has_syntax(); return syntax_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -2470,8 +2490,17 @@ DescriptorProto_ExtensionRange* DescriptorProto_ExtensionRange::New(::google::pr } void DescriptorProto_ExtensionRange::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.DescriptorProto.ExtensionRange) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(DescriptorProto_ExtensionRange, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -2491,7 +2520,7 @@ void DescriptorProto_ExtensionRange::Clear() { bool DescriptorProto_ExtensionRange::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.DescriptorProto.ExtensionRange) for (;;) { @@ -2592,6 +2621,7 @@ void DescriptorProto_ExtensionRange::SerializeWithCachedSizes( } int DescriptorProto_ExtensionRange::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.DescriptorProto.ExtensionRange) int total_size = 0; if (_has_bits_[0 / 32] & 3u) { @@ -2622,18 +2652,22 @@ int DescriptorProto_ExtensionRange::ByteSize() const { } void DescriptorProto_ExtensionRange::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.DescriptorProto.ExtensionRange) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const DescriptorProto_ExtensionRange* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.DescriptorProto.ExtensionRange) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.DescriptorProto.ExtensionRange) MergeFrom(*source); } } void DescriptorProto_ExtensionRange::MergeFrom(const DescriptorProto_ExtensionRange& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.DescriptorProto.ExtensionRange) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { if (from.has_start()) { @@ -2649,12 +2683,14 @@ void DescriptorProto_ExtensionRange::MergeFrom(const DescriptorProto_ExtensionRa } void DescriptorProto_ExtensionRange::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.DescriptorProto.ExtensionRange) if (&from == this) return; Clear(); MergeFrom(from); } void DescriptorProto_ExtensionRange::CopyFrom(const DescriptorProto_ExtensionRange& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.DescriptorProto.ExtensionRange) if (&from == this) return; Clear(); MergeFrom(from); @@ -2753,8 +2789,17 @@ DescriptorProto_ReservedRange* DescriptorProto_ReservedRange::New(::google::prot } void DescriptorProto_ReservedRange::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.DescriptorProto.ReservedRange) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(DescriptorProto_ReservedRange, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -2774,7 +2819,7 @@ void DescriptorProto_ReservedRange::Clear() { bool DescriptorProto_ReservedRange::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.DescriptorProto.ReservedRange) for (;;) { @@ -2875,6 +2920,7 @@ void DescriptorProto_ReservedRange::SerializeWithCachedSizes( } int DescriptorProto_ReservedRange::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.DescriptorProto.ReservedRange) int total_size = 0; if (_has_bits_[0 / 32] & 3u) { @@ -2905,18 +2951,22 @@ int DescriptorProto_ReservedRange::ByteSize() const { } void DescriptorProto_ReservedRange::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.DescriptorProto.ReservedRange) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const DescriptorProto_ReservedRange* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.DescriptorProto.ReservedRange) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.DescriptorProto.ReservedRange) MergeFrom(*source); } } void DescriptorProto_ReservedRange::MergeFrom(const DescriptorProto_ReservedRange& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.DescriptorProto.ReservedRange) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { if (from.has_start()) { @@ -2932,12 +2982,14 @@ void DescriptorProto_ReservedRange::MergeFrom(const DescriptorProto_ReservedRang } void DescriptorProto_ReservedRange::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.DescriptorProto.ReservedRange) if (&from == this) return; Clear(); MergeFrom(from); } void DescriptorProto_ReservedRange::CopyFrom(const DescriptorProto_ReservedRange& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.DescriptorProto.ReservedRange) if (&from == this) return; Clear(); MergeFrom(from); @@ -3048,6 +3100,7 @@ DescriptorProto* DescriptorProto::New(::google::protobuf::Arena* arena) const { } void DescriptorProto::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.DescriptorProto) if (_has_bits_[0 / 32] & 129u) { if (has_name()) { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); @@ -3072,7 +3125,7 @@ void DescriptorProto::Clear() { bool DescriptorProto::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.DescriptorProto) for (;;) { @@ -3431,6 +3484,7 @@ void DescriptorProto::SerializeWithCachedSizes( } int DescriptorProto::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.DescriptorProto) int total_size = 0; if (_has_bits_[0 / 32] & 129u) { @@ -3524,18 +3578,22 @@ int DescriptorProto::ByteSize() const { } void DescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.DescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const DescriptorProto* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.DescriptorProto) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.DescriptorProto) MergeFrom(*source); } } void DescriptorProto::MergeFrom(const DescriptorProto& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.DescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); field_.MergeFrom(from.field_); extension_.MergeFrom(from.extension_); @@ -3560,12 +3618,14 @@ void DescriptorProto::MergeFrom(const DescriptorProto& from) { } void DescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.DescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); } void DescriptorProto::CopyFrom(const DescriptorProto& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.DescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); @@ -3758,6 +3818,7 @@ void DescriptorProto::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* DescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.DescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -3978,6 +4039,7 @@ const ::google::protobuf::MessageOptions& DescriptorProto::options() const { return options_; } ::google::protobuf::MessageOptions* DescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.DescriptorProto.options) clear_has_options(); ::google::protobuf::MessageOptions* temp = options_; options_ = NULL; @@ -4053,6 +4115,7 @@ void DescriptorProto::clear_reserved_name() { // @@protoc_insertion_point(field_set_pointer:google.protobuf.DescriptorProto.reserved_name) } ::std::string* DescriptorProto::add_reserved_name() { + // @@protoc_insertion_point(field_add_mutable:google.protobuf.DescriptorProto.reserved_name) return reserved_name_.Add(); } void DescriptorProto::add_reserved_name(const ::std::string& value) { @@ -4247,6 +4310,7 @@ FieldDescriptorProto* FieldDescriptorProto::New(::google::protobuf::Arena* arena } void FieldDescriptorProto::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.FieldDescriptorProto) if (_has_bits_[0 / 32] & 255u) { if (has_name()) { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); @@ -4281,7 +4345,7 @@ void FieldDescriptorProto::Clear() { bool FieldDescriptorProto::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.FieldDescriptorProto) for (;;) { @@ -4662,6 +4726,7 @@ void FieldDescriptorProto::SerializeWithCachedSizes( } int FieldDescriptorProto::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FieldDescriptorProto) int total_size = 0; if (_has_bits_[0 / 32] & 255u) { @@ -4748,18 +4813,22 @@ int FieldDescriptorProto::ByteSize() const { } void FieldDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.FieldDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const FieldDescriptorProto* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.FieldDescriptorProto) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.FieldDescriptorProto) MergeFrom(*source); } } void FieldDescriptorProto::MergeFrom(const FieldDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FieldDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { if (from.has_name()) { @@ -4806,12 +4875,14 @@ void FieldDescriptorProto::MergeFrom(const FieldDescriptorProto& from) { } void FieldDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.FieldDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); } void FieldDescriptorProto::CopyFrom(const FieldDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FieldDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); @@ -4896,6 +4967,7 @@ void FieldDescriptorProto::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* FieldDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5023,6 +5095,7 @@ void FieldDescriptorProto::clear_type_name() { return type_name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* FieldDescriptorProto::release_type_name() { + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.type_name) clear_has_type_name(); return type_name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5076,6 +5149,7 @@ void FieldDescriptorProto::clear_extendee() { return extendee_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* FieldDescriptorProto::release_extendee() { + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.extendee) clear_has_extendee(); return extendee_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5129,6 +5203,7 @@ void FieldDescriptorProto::clear_default_value() { return default_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* FieldDescriptorProto::release_default_value() { + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.default_value) clear_has_default_value(); return default_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5206,6 +5281,7 @@ void FieldDescriptorProto::clear_json_name() { return json_name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* FieldDescriptorProto::release_json_name() { + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.json_name) clear_has_json_name(); return json_name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5246,6 +5322,7 @@ const ::google::protobuf::FieldOptions& FieldDescriptorProto::options() const { return options_; } ::google::protobuf::FieldOptions* FieldDescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.options) clear_has_options(); ::google::protobuf::FieldOptions* temp = options_; options_ = NULL; @@ -5331,6 +5408,7 @@ OneofDescriptorProto* OneofDescriptorProto::New(::google::protobuf::Arena* arena } void OneofDescriptorProto::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.OneofDescriptorProto) if (has_name()) { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5342,7 +5420,7 @@ void OneofDescriptorProto::Clear() { bool OneofDescriptorProto::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.OneofDescriptorProto) for (;;) { @@ -5431,6 +5509,7 @@ void OneofDescriptorProto::SerializeWithCachedSizes( } int OneofDescriptorProto::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.OneofDescriptorProto) int total_size = 0; // optional string name = 1; @@ -5452,18 +5531,22 @@ int OneofDescriptorProto::ByteSize() const { } void OneofDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.OneofDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const OneofDescriptorProto* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.OneofDescriptorProto) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.OneofDescriptorProto) MergeFrom(*source); } } void OneofDescriptorProto::MergeFrom(const OneofDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.OneofDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { if (from.has_name()) { @@ -5477,12 +5560,14 @@ void OneofDescriptorProto::MergeFrom(const OneofDescriptorProto& from) { } void OneofDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.OneofDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); } void OneofDescriptorProto::CopyFrom(const OneofDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.OneofDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); @@ -5555,6 +5640,7 @@ void OneofDescriptorProto::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* OneofDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.OneofDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5642,6 +5728,7 @@ EnumDescriptorProto* EnumDescriptorProto::New(::google::protobuf::Arena* arena) } void EnumDescriptorProto::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.EnumDescriptorProto) if (_has_bits_[0 / 32] & 5u) { if (has_name()) { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); @@ -5659,7 +5746,7 @@ void EnumDescriptorProto::Clear() { bool EnumDescriptorProto::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.EnumDescriptorProto) for (;;) { @@ -5804,6 +5891,7 @@ void EnumDescriptorProto::SerializeWithCachedSizes( } int EnumDescriptorProto::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.EnumDescriptorProto) int total_size = 0; if (_has_bits_[0 / 32] & 5u) { @@ -5842,18 +5930,22 @@ int EnumDescriptorProto::ByteSize() const { } void EnumDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.EnumDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const EnumDescriptorProto* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.EnumDescriptorProto) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.EnumDescriptorProto) MergeFrom(*source); } } void EnumDescriptorProto::MergeFrom(const EnumDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.EnumDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); value_.MergeFrom(from.value_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { @@ -5871,12 +5963,14 @@ void EnumDescriptorProto::MergeFrom(const EnumDescriptorProto& from) { } void EnumDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.EnumDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); } void EnumDescriptorProto::CopyFrom(const EnumDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.EnumDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); @@ -5955,6 +6049,7 @@ void EnumDescriptorProto::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* EnumDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.EnumDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -6025,6 +6120,7 @@ const ::google::protobuf::EnumOptions& EnumDescriptorProto::options() const { return options_; } ::google::protobuf::EnumOptions* EnumDescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.EnumDescriptorProto.options) clear_has_options(); ::google::protobuf::EnumOptions* temp = options_; options_ = NULL; @@ -6116,6 +6212,7 @@ EnumValueDescriptorProto* EnumValueDescriptorProto::New(::google::protobuf::Aren } void EnumValueDescriptorProto::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.EnumValueDescriptorProto) if (_has_bits_[0 / 32] & 7u) { if (has_name()) { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); @@ -6133,7 +6230,7 @@ void EnumValueDescriptorProto::Clear() { bool EnumValueDescriptorProto::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.EnumValueDescriptorProto) for (;;) { @@ -6273,6 +6370,7 @@ void EnumValueDescriptorProto::SerializeWithCachedSizes( } int EnumValueDescriptorProto::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.EnumValueDescriptorProto) int total_size = 0; if (_has_bits_[0 / 32] & 7u) { @@ -6310,18 +6408,22 @@ int EnumValueDescriptorProto::ByteSize() const { } void EnumValueDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.EnumValueDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const EnumValueDescriptorProto* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.EnumValueDescriptorProto) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.EnumValueDescriptorProto) MergeFrom(*source); } } void EnumValueDescriptorProto::MergeFrom(const EnumValueDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.EnumValueDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { if (from.has_name()) { @@ -6341,12 +6443,14 @@ void EnumValueDescriptorProto::MergeFrom(const EnumValueDescriptorProto& from) { } void EnumValueDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.EnumValueDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); } void EnumValueDescriptorProto::CopyFrom(const EnumValueDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.EnumValueDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); @@ -6424,6 +6528,7 @@ void EnumValueDescriptorProto::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* EnumValueDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.EnumValueDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -6488,6 +6593,7 @@ const ::google::protobuf::EnumValueOptions& EnumValueDescriptorProto::options() return options_; } ::google::protobuf::EnumValueOptions* EnumValueDescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.EnumValueDescriptorProto.options) clear_has_options(); ::google::protobuf::EnumValueOptions* temp = options_; options_ = NULL; @@ -6578,6 +6684,7 @@ ServiceDescriptorProto* ServiceDescriptorProto::New(::google::protobuf::Arena* a } void ServiceDescriptorProto::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.ServiceDescriptorProto) if (_has_bits_[0 / 32] & 5u) { if (has_name()) { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); @@ -6595,7 +6702,7 @@ void ServiceDescriptorProto::Clear() { bool ServiceDescriptorProto::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.ServiceDescriptorProto) for (;;) { @@ -6740,6 +6847,7 @@ void ServiceDescriptorProto::SerializeWithCachedSizes( } int ServiceDescriptorProto::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.ServiceDescriptorProto) int total_size = 0; if (_has_bits_[0 / 32] & 5u) { @@ -6778,18 +6886,22 @@ int ServiceDescriptorProto::ByteSize() const { } void ServiceDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.ServiceDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const ServiceDescriptorProto* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.ServiceDescriptorProto) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.ServiceDescriptorProto) MergeFrom(*source); } } void ServiceDescriptorProto::MergeFrom(const ServiceDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.ServiceDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); method_.MergeFrom(from.method_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { @@ -6807,12 +6919,14 @@ void ServiceDescriptorProto::MergeFrom(const ServiceDescriptorProto& from) { } void ServiceDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.ServiceDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); } void ServiceDescriptorProto::CopyFrom(const ServiceDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.ServiceDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); @@ -6891,6 +7005,7 @@ void ServiceDescriptorProto::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* ServiceDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.ServiceDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -6961,6 +7076,7 @@ const ::google::protobuf::ServiceOptions& ServiceDescriptorProto::options() cons return options_; } ::google::protobuf::ServiceOptions* ServiceDescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.ServiceDescriptorProto.options) clear_has_options(); ::google::protobuf::ServiceOptions* temp = options_; options_ = NULL; @@ -7060,8 +7176,17 @@ MethodDescriptorProto* MethodDescriptorProto::New(::google::protobuf::Arena* are } void MethodDescriptorProto::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.MethodDescriptorProto) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(MethodDescriptorProto, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -7095,7 +7220,7 @@ void MethodDescriptorProto::Clear() { bool MethodDescriptorProto::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.MethodDescriptorProto) for (;;) { @@ -7336,6 +7461,7 @@ void MethodDescriptorProto::SerializeWithCachedSizes( } int MethodDescriptorProto::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.MethodDescriptorProto) int total_size = 0; if (_has_bits_[0 / 32] & 63u) { @@ -7390,18 +7516,22 @@ int MethodDescriptorProto::ByteSize() const { } void MethodDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.MethodDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const MethodDescriptorProto* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.MethodDescriptorProto) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.MethodDescriptorProto) MergeFrom(*source); } } void MethodDescriptorProto::MergeFrom(const MethodDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.MethodDescriptorProto) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { if (from.has_name()) { @@ -7432,12 +7562,14 @@ void MethodDescriptorProto::MergeFrom(const MethodDescriptorProto& from) { } void MethodDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.MethodDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); } void MethodDescriptorProto::CopyFrom(const MethodDescriptorProto& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.MethodDescriptorProto) if (&from == this) return; Clear(); MergeFrom(from); @@ -7518,6 +7650,7 @@ void MethodDescriptorProto::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* MethodDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -7571,6 +7704,7 @@ void MethodDescriptorProto::clear_input_type() { return input_type_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* MethodDescriptorProto::release_input_type() { + // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.input_type) clear_has_input_type(); return input_type_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -7624,6 +7758,7 @@ void MethodDescriptorProto::clear_output_type() { return output_type_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* MethodDescriptorProto::release_output_type() { + // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.output_type) clear_has_output_type(); return output_type_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -7664,6 +7799,7 @@ const ::google::protobuf::MethodOptions& MethodDescriptorProto::options() const return options_; } ::google::protobuf::MethodOptions* MethodDescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.options) clear_has_options(); ::google::protobuf::MethodOptions* temp = options_; options_ = NULL; @@ -7770,7 +7906,6 @@ const int FileOptions::kDeprecatedFieldNumber; const int FileOptions::kCcEnableArenasFieldNumber; const int FileOptions::kObjcClassPrefixFieldNumber; const int FileOptions::kCsharpNamespaceFieldNumber; -const int FileOptions::kJavananoUseDeprecatedPackageFieldNumber; const int FileOptions::kUninterpretedOptionFieldNumber; #endif // !defined(_MSC_VER) || _MSC_VER >= 1900 @@ -7808,7 +7943,6 @@ void FileOptions::SharedCtor() { cc_enable_arenas_ = false; objc_class_prefix_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); csharp_namespace_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); - javanano_use_deprecated_package_ = false; ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -7853,9 +7987,18 @@ FileOptions* FileOptions::New(::google::protobuf::Arena* arena) const { } void FileOptions::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.FileOptions) _extensions_.Clear(); +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(FileOptions, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -7875,8 +8018,8 @@ void FileOptions::Clear() { go_package_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } } - if (_has_bits_[8 / 32] & 32512u) { - ZR_(java_generic_services_, javanano_use_deprecated_package_); + if (_has_bits_[8 / 32] & 16128u) { + ZR_(java_generic_services_, cc_enable_arenas_); if (has_objc_class_prefix()) { objc_class_prefix_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -7897,7 +8040,7 @@ void FileOptions::Clear() { bool FileOptions::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.FileOptions) for (;;) { @@ -8125,21 +8268,6 @@ bool FileOptions::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(304)) goto parse_javanano_use_deprecated_package; - break; - } - - // optional bool javanano_use_deprecated_package = 38 [deprecated = true]; - case 38: { - if (tag == 304) { - parse_javanano_use_deprecated_package: - DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< - bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>( - input, &javanano_use_deprecated_package_))); - set_has_javanano_use_deprecated_package(); - } else { - goto handle_unusual; - } if (input->ExpectTag(7994)) goto parse_uninterpreted_option; break; } @@ -8287,11 +8415,6 @@ void FileOptions::SerializeWithCachedSizes( 37, this->csharp_namespace(), output); } - // optional bool javanano_use_deprecated_package = 38 [deprecated = true]; - if (has_javanano_use_deprecated_package()) { - ::google::protobuf::internal::WireFormatLite::WriteBool(38, this->javanano_use_deprecated_package(), output); - } - // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; for (unsigned int i = 0, n = this->uninterpreted_option_size(); i < n; i++) { ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( @@ -8413,11 +8536,6 @@ void FileOptions::SerializeWithCachedSizes( 37, this->csharp_namespace(), target); } - // optional bool javanano_use_deprecated_package = 38 [deprecated = true]; - if (has_javanano_use_deprecated_package()) { - target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(38, this->javanano_use_deprecated_package(), target); - } - // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; for (unsigned int i = 0, n = this->uninterpreted_option_size(); i < n; i++) { target = ::google::protobuf::internal::WireFormatLite:: @@ -8438,6 +8556,7 @@ void FileOptions::SerializeWithCachedSizes( } int FileOptions::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FileOptions) int total_size = 0; if (_has_bits_[0 / 32] & 255u) { @@ -8489,7 +8608,7 @@ int FileOptions::ByteSize() const { } } - if (_has_bits_[8 / 32] & 32512u) { + if (_has_bits_[8 / 32] & 16128u) { // optional bool java_generic_services = 17 [default = false]; if (has_java_generic_services()) { total_size += 2 + 1; @@ -8524,11 +8643,6 @@ int FileOptions::ByteSize() const { this->csharp_namespace()); } - // optional bool javanano_use_deprecated_package = 38 [deprecated = true]; - if (has_javanano_use_deprecated_package()) { - total_size += 2 + 1; - } - } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; total_size += 2 * this->uninterpreted_option_size(); @@ -8552,18 +8666,22 @@ int FileOptions::ByteSize() const { } void FileOptions::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.FileOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const FileOptions* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.FileOptions) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.FileOptions) MergeFrom(*source); } } void FileOptions::MergeFrom(const FileOptions& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FileOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); uninterpreted_option_.MergeFrom(from.uninterpreted_option_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { @@ -8616,9 +8734,6 @@ void FileOptions::MergeFrom(const FileOptions& from) { set_has_csharp_namespace(); csharp_namespace_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.csharp_namespace_); } - if (from.has_javanano_use_deprecated_package()) { - set_javanano_use_deprecated_package(from.javanano_use_deprecated_package()); - } } _extensions_.MergeFrom(from._extensions_); if (from._internal_metadata_.have_unknown_fields()) { @@ -8627,12 +8742,14 @@ void FileOptions::MergeFrom(const FileOptions& from) { } void FileOptions::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.FileOptions) if (&from == this) return; Clear(); MergeFrom(from); } void FileOptions::CopyFrom(const FileOptions& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FileOptions) if (&from == this) return; Clear(); MergeFrom(from); @@ -8664,7 +8781,6 @@ void FileOptions::InternalSwap(FileOptions* other) { std::swap(cc_enable_arenas_, other->cc_enable_arenas_); objc_class_prefix_.Swap(&other->objc_class_prefix_); csharp_namespace_.Swap(&other->csharp_namespace_); - std::swap(javanano_use_deprecated_package_, other->javanano_use_deprecated_package_); uninterpreted_option_.UnsafeArenaSwap(&other->uninterpreted_option_); std::swap(_has_bits_[0], other->_has_bits_[0]); _internal_metadata_.Swap(&other->_internal_metadata_); @@ -8723,6 +8839,7 @@ void FileOptions::clear_java_package() { return java_package_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* FileOptions::release_java_package() { + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.java_package) clear_has_java_package(); return java_package_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -8776,6 +8893,7 @@ void FileOptions::clear_java_outer_classname() { return java_outer_classname_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* FileOptions::release_java_outer_classname() { + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.java_outer_classname) clear_has_java_outer_classname(); return java_outer_classname_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -8926,6 +9044,7 @@ void FileOptions::clear_go_package() { return go_package_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* FileOptions::release_go_package() { + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.go_package) clear_has_go_package(); return go_package_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -9099,6 +9218,7 @@ void FileOptions::clear_objc_class_prefix() { return objc_class_prefix_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* FileOptions::release_objc_class_prefix() { + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.objc_class_prefix) clear_has_objc_class_prefix(); return objc_class_prefix_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -9152,6 +9272,7 @@ void FileOptions::clear_csharp_namespace() { return csharp_namespace_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* FileOptions::release_csharp_namespace() { + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.csharp_namespace) clear_has_csharp_namespace(); return csharp_namespace_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -9165,30 +9286,6 @@ void FileOptions::clear_csharp_namespace() { // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.csharp_namespace) } -// optional bool javanano_use_deprecated_package = 38 [deprecated = true]; -bool FileOptions::has_javanano_use_deprecated_package() const { - return (_has_bits_[0] & 0x00004000u) != 0; -} -void FileOptions::set_has_javanano_use_deprecated_package() { - _has_bits_[0] |= 0x00004000u; -} -void FileOptions::clear_has_javanano_use_deprecated_package() { - _has_bits_[0] &= ~0x00004000u; -} -void FileOptions::clear_javanano_use_deprecated_package() { - javanano_use_deprecated_package_ = false; - clear_has_javanano_use_deprecated_package(); -} - bool FileOptions::javanano_use_deprecated_package() const { - // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.javanano_use_deprecated_package) - return javanano_use_deprecated_package_; -} - void FileOptions::set_javanano_use_deprecated_package(bool value) { - set_has_javanano_use_deprecated_package(); - javanano_use_deprecated_package_ = value; - // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.javanano_use_deprecated_package) -} - // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; int FileOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); @@ -9293,9 +9390,18 @@ MessageOptions* MessageOptions::New(::google::protobuf::Arena* arena) const { } void MessageOptions::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.MessageOptions) _extensions_.Clear(); +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(MessageOptions, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -9316,7 +9422,7 @@ void MessageOptions::Clear() { bool MessageOptions::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.MessageOptions) for (;;) { @@ -9510,6 +9616,7 @@ void MessageOptions::SerializeWithCachedSizes( } int MessageOptions::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.MessageOptions) int total_size = 0; if (_has_bits_[0 / 32] & 15u) { @@ -9556,18 +9663,22 @@ int MessageOptions::ByteSize() const { } void MessageOptions::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.MessageOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const MessageOptions* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.MessageOptions) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.MessageOptions) MergeFrom(*source); } } void MessageOptions::MergeFrom(const MessageOptions& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.MessageOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); uninterpreted_option_.MergeFrom(from.uninterpreted_option_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { @@ -9591,12 +9702,14 @@ void MessageOptions::MergeFrom(const MessageOptions& from) { } void MessageOptions::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.MessageOptions) if (&from == this) return; Clear(); MergeFrom(from); } void MessageOptions::CopyFrom(const MessageOptions& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.MessageOptions) if (&from == this) return; Clear(); MergeFrom(from); @@ -9886,9 +9999,18 @@ FieldOptions* FieldOptions::New(::google::protobuf::Arena* arena) const { } void FieldOptions::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.FieldOptions) _extensions_.Clear(); +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(FieldOptions, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -9912,7 +10034,7 @@ void FieldOptions::Clear() { bool FieldOptions::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.FieldOptions) for (;;) { @@ -10170,6 +10292,7 @@ void FieldOptions::SerializeWithCachedSizes( } int FieldOptions::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FieldOptions) int total_size = 0; if (_has_bits_[0 / 32] & 63u) { @@ -10228,18 +10351,22 @@ int FieldOptions::ByteSize() const { } void FieldOptions::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.FieldOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const FieldOptions* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.FieldOptions) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.FieldOptions) MergeFrom(*source); } } void FieldOptions::MergeFrom(const FieldOptions& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FieldOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); uninterpreted_option_.MergeFrom(from.uninterpreted_option_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { @@ -10269,12 +10396,14 @@ void FieldOptions::MergeFrom(const FieldOptions& from) { } void FieldOptions::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.FieldOptions) if (&from == this) return; Clear(); MergeFrom(from); } void FieldOptions::CopyFrom(const FieldOptions& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FieldOptions) if (&from == this) return; Clear(); MergeFrom(from); @@ -10562,9 +10691,18 @@ EnumOptions* EnumOptions::New(::google::protobuf::Arena* arena) const { } void EnumOptions::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.EnumOptions) _extensions_.Clear(); +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(EnumOptions, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -10585,7 +10723,7 @@ void EnumOptions::Clear() { bool EnumOptions::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.EnumOptions) for (;;) { @@ -10729,6 +10867,7 @@ void EnumOptions::SerializeWithCachedSizes( } int EnumOptions::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.EnumOptions) int total_size = 0; if (_has_bits_[0 / 32] & 3u) { @@ -10765,18 +10904,22 @@ int EnumOptions::ByteSize() const { } void EnumOptions::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.EnumOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const EnumOptions* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.EnumOptions) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.EnumOptions) MergeFrom(*source); } } void EnumOptions::MergeFrom(const EnumOptions& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.EnumOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); uninterpreted_option_.MergeFrom(from.uninterpreted_option_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { @@ -10794,12 +10937,14 @@ void EnumOptions::MergeFrom(const EnumOptions& from) { } void EnumOptions::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.EnumOptions) if (&from == this) return; Clear(); MergeFrom(from); } void EnumOptions::CopyFrom(const EnumOptions& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.EnumOptions) if (&from == this) return; Clear(); MergeFrom(from); @@ -10983,6 +11128,7 @@ EnumValueOptions* EnumValueOptions::New(::google::protobuf::Arena* arena) const } void EnumValueOptions::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.EnumValueOptions) _extensions_.Clear(); deprecated_ = false; uninterpreted_option_.Clear(); @@ -10994,7 +11140,7 @@ void EnumValueOptions::Clear() { bool EnumValueOptions::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.EnumValueOptions) for (;;) { @@ -11113,6 +11259,7 @@ void EnumValueOptions::SerializeWithCachedSizes( } int EnumValueOptions::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.EnumValueOptions) int total_size = 0; // optional bool deprecated = 1 [default = false]; @@ -11142,18 +11289,22 @@ int EnumValueOptions::ByteSize() const { } void EnumValueOptions::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.EnumValueOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const EnumValueOptions* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.EnumValueOptions) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.EnumValueOptions) MergeFrom(*source); } } void EnumValueOptions::MergeFrom(const EnumValueOptions& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.EnumValueOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); uninterpreted_option_.MergeFrom(from.uninterpreted_option_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { @@ -11168,12 +11319,14 @@ void EnumValueOptions::MergeFrom(const EnumValueOptions& from) { } void EnumValueOptions::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.EnumValueOptions) if (&from == this) return; Clear(); MergeFrom(from); } void EnumValueOptions::CopyFrom(const EnumValueOptions& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.EnumValueOptions) if (&from == this) return; Clear(); MergeFrom(from); @@ -11332,6 +11485,7 @@ ServiceOptions* ServiceOptions::New(::google::protobuf::Arena* arena) const { } void ServiceOptions::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.ServiceOptions) _extensions_.Clear(); deprecated_ = false; uninterpreted_option_.Clear(); @@ -11343,7 +11497,7 @@ void ServiceOptions::Clear() { bool ServiceOptions::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.ServiceOptions) for (;;) { @@ -11462,6 +11616,7 @@ void ServiceOptions::SerializeWithCachedSizes( } int ServiceOptions::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.ServiceOptions) int total_size = 0; // optional bool deprecated = 33 [default = false]; @@ -11491,18 +11646,22 @@ int ServiceOptions::ByteSize() const { } void ServiceOptions::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.ServiceOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const ServiceOptions* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.ServiceOptions) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.ServiceOptions) MergeFrom(*source); } } void ServiceOptions::MergeFrom(const ServiceOptions& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.ServiceOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); uninterpreted_option_.MergeFrom(from.uninterpreted_option_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { @@ -11517,12 +11676,14 @@ void ServiceOptions::MergeFrom(const ServiceOptions& from) { } void ServiceOptions::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.ServiceOptions) if (&from == this) return; Clear(); MergeFrom(from); } void ServiceOptions::CopyFrom(const ServiceOptions& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.ServiceOptions) if (&from == this) return; Clear(); MergeFrom(from); @@ -11681,6 +11842,7 @@ MethodOptions* MethodOptions::New(::google::protobuf::Arena* arena) const { } void MethodOptions::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.MethodOptions) _extensions_.Clear(); deprecated_ = false; uninterpreted_option_.Clear(); @@ -11692,7 +11854,7 @@ void MethodOptions::Clear() { bool MethodOptions::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.MethodOptions) for (;;) { @@ -11811,6 +11973,7 @@ void MethodOptions::SerializeWithCachedSizes( } int MethodOptions::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.MethodOptions) int total_size = 0; // optional bool deprecated = 33 [default = false]; @@ -11840,18 +12003,22 @@ int MethodOptions::ByteSize() const { } void MethodOptions::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.MethodOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const MethodOptions* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.MethodOptions) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.MethodOptions) MergeFrom(*source); } } void MethodOptions::MergeFrom(const MethodOptions& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.MethodOptions) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); uninterpreted_option_.MergeFrom(from.uninterpreted_option_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { @@ -11866,12 +12033,14 @@ void MethodOptions::MergeFrom(const MethodOptions& from) { } void MethodOptions::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.MethodOptions) if (&from == this) return; Clear(); MergeFrom(from); } void MethodOptions::CopyFrom(const MethodOptions& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.MethodOptions) if (&from == this) return; Clear(); MergeFrom(from); @@ -12033,6 +12202,7 @@ UninterpretedOption_NamePart* UninterpretedOption_NamePart::New(::google::protob } void UninterpretedOption_NamePart::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.UninterpretedOption.NamePart) if (_has_bits_[0 / 32] & 3u) { if (has_name_part()) { name_part_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); @@ -12047,7 +12217,7 @@ void UninterpretedOption_NamePart::Clear() { bool UninterpretedOption_NamePart::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.UninterpretedOption.NamePart) for (;;) { @@ -12161,6 +12331,7 @@ void UninterpretedOption_NamePart::SerializeWithCachedSizes( } int UninterpretedOption_NamePart::RequiredFieldsByteSizeFallback() const { +// @@protoc_insertion_point(required_fields_byte_size_fallback_start:google.protobuf.UninterpretedOption.NamePart) int total_size = 0; if (has_name_part()) { @@ -12178,6 +12349,7 @@ int UninterpretedOption_NamePart::RequiredFieldsByteSizeFallback() const { return total_size; } int UninterpretedOption_NamePart::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.UninterpretedOption.NamePart) int total_size = 0; if (((_has_bits_[0] & 0x00000003) ^ 0x00000003) == 0) { // All required fields are present. @@ -12204,18 +12376,22 @@ int UninterpretedOption_NamePart::ByteSize() const { } void UninterpretedOption_NamePart::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.UninterpretedOption.NamePart) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const UninterpretedOption_NamePart* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.UninterpretedOption.NamePart) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.UninterpretedOption.NamePart) MergeFrom(*source); } } void UninterpretedOption_NamePart::MergeFrom(const UninterpretedOption_NamePart& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.UninterpretedOption.NamePart) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { if (from.has_name_part()) { @@ -12232,12 +12408,14 @@ void UninterpretedOption_NamePart::MergeFrom(const UninterpretedOption_NamePart& } void UninterpretedOption_NamePart::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.UninterpretedOption.NamePart) if (&from == this) return; Clear(); MergeFrom(from); } void UninterpretedOption_NamePart::CopyFrom(const UninterpretedOption_NamePart& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.UninterpretedOption.NamePart) if (&from == this) return; Clear(); MergeFrom(from); @@ -12350,8 +12528,17 @@ UninterpretedOption* UninterpretedOption::New(::google::protobuf::Arena* arena) } void UninterpretedOption::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.UninterpretedOption) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(UninterpretedOption, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -12383,7 +12570,7 @@ void UninterpretedOption::Clear() { bool UninterpretedOption::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.UninterpretedOption) for (;;) { @@ -12641,6 +12828,7 @@ void UninterpretedOption::SerializeWithCachedSizes( } int UninterpretedOption::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.UninterpretedOption) int total_size = 0; if (_has_bits_[1 / 32] & 126u) { @@ -12705,18 +12893,22 @@ int UninterpretedOption::ByteSize() const { } void UninterpretedOption::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.UninterpretedOption) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const UninterpretedOption* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.UninterpretedOption) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.UninterpretedOption) MergeFrom(*source); } } void UninterpretedOption::MergeFrom(const UninterpretedOption& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.UninterpretedOption) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); name_.MergeFrom(from.name_); if (from._has_bits_[1 / 32] & (0xffu << (1 % 32))) { @@ -12748,12 +12940,14 @@ void UninterpretedOption::MergeFrom(const UninterpretedOption& from) { } void UninterpretedOption::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.UninterpretedOption) if (&from == this) return; Clear(); MergeFrom(from); } void UninterpretedOption::CopyFrom(const UninterpretedOption& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.UninterpretedOption) if (&from == this) return; Clear(); MergeFrom(from); @@ -12833,6 +13027,7 @@ void UninterpretedOption_NamePart::clear_name_part() { return name_part_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* UninterpretedOption_NamePart::release_name_part() { + // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.NamePart.name_part) clear_has_name_part(); return name_part_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -12944,6 +13139,7 @@ void UninterpretedOption::clear_identifier_value() { return identifier_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* UninterpretedOption::release_identifier_value() { + // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.identifier_value) clear_has_identifier_value(); return identifier_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -13069,6 +13265,7 @@ void UninterpretedOption::clear_string_value() { return string_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* UninterpretedOption::release_string_value() { + // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.string_value) clear_has_string_value(); return string_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -13122,6 +13319,7 @@ void UninterpretedOption::clear_aggregate_value() { return aggregate_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* UninterpretedOption::release_aggregate_value() { + // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.aggregate_value) clear_has_aggregate_value(); return aggregate_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -13210,6 +13408,7 @@ SourceCodeInfo_Location* SourceCodeInfo_Location::New(::google::protobuf::Arena* } void SourceCodeInfo_Location::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.SourceCodeInfo.Location) if (_has_bits_[0 / 32] & 12u) { if (has_leading_comments()) { leading_comments_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); @@ -13229,7 +13428,7 @@ void SourceCodeInfo_Location::Clear() { bool SourceCodeInfo_Location::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.SourceCodeInfo.Location) for (;;) { @@ -13479,6 +13678,7 @@ void SourceCodeInfo_Location::SerializeWithCachedSizes( } int SourceCodeInfo_Location::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.SourceCodeInfo.Location) int total_size = 0; if (_has_bits_[2 / 32] & 12u) { @@ -13550,18 +13750,22 @@ int SourceCodeInfo_Location::ByteSize() const { } void SourceCodeInfo_Location::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.SourceCodeInfo.Location) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const SourceCodeInfo_Location* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.SourceCodeInfo.Location) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.SourceCodeInfo.Location) MergeFrom(*source); } } void SourceCodeInfo_Location::MergeFrom(const SourceCodeInfo_Location& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.SourceCodeInfo.Location) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); path_.MergeFrom(from.path_); span_.MergeFrom(from.span_); @@ -13582,12 +13786,14 @@ void SourceCodeInfo_Location::MergeFrom(const SourceCodeInfo_Location& from) { } void SourceCodeInfo_Location::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.SourceCodeInfo.Location) if (&from == this) return; Clear(); MergeFrom(from); } void SourceCodeInfo_Location::CopyFrom(const SourceCodeInfo_Location& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.SourceCodeInfo.Location) if (&from == this) return; Clear(); MergeFrom(from); @@ -13686,6 +13892,7 @@ SourceCodeInfo* SourceCodeInfo::New(::google::protobuf::Arena* arena) const { } void SourceCodeInfo::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.SourceCodeInfo) location_.Clear(); ::memset(_has_bits_, 0, sizeof(_has_bits_)); if (_internal_metadata_.have_unknown_fields()) { @@ -13695,7 +13902,7 @@ void SourceCodeInfo::Clear() { bool SourceCodeInfo::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.SourceCodeInfo) for (;;) { @@ -13776,6 +13983,7 @@ void SourceCodeInfo::SerializeWithCachedSizes( } int SourceCodeInfo::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.SourceCodeInfo) int total_size = 0; // repeated .google.protobuf.SourceCodeInfo.Location location = 1; @@ -13798,18 +14006,22 @@ int SourceCodeInfo::ByteSize() const { } void SourceCodeInfo::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.SourceCodeInfo) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const SourceCodeInfo* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.SourceCodeInfo) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.SourceCodeInfo) MergeFrom(*source); } } void SourceCodeInfo::MergeFrom(const SourceCodeInfo& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.SourceCodeInfo) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); location_.MergeFrom(from.location_); if (from._internal_metadata_.have_unknown_fields()) { @@ -13818,12 +14030,14 @@ void SourceCodeInfo::MergeFrom(const SourceCodeInfo& from) { } void SourceCodeInfo::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.SourceCodeInfo) if (&from == this) return; Clear(); MergeFrom(from); } void SourceCodeInfo::CopyFrom(const SourceCodeInfo& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.SourceCodeInfo) if (&from == this) return; Clear(); MergeFrom(from); @@ -13956,6 +14170,7 @@ void SourceCodeInfo_Location::clear_leading_comments() { return leading_comments_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* SourceCodeInfo_Location::release_leading_comments() { + // @@protoc_insertion_point(field_release:google.protobuf.SourceCodeInfo.Location.leading_comments) clear_has_leading_comments(); return leading_comments_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -14009,6 +14224,7 @@ void SourceCodeInfo_Location::clear_trailing_comments() { return trailing_comments_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* SourceCodeInfo_Location::release_trailing_comments() { + // @@protoc_insertion_point(field_release:google.protobuf.SourceCodeInfo.Location.trailing_comments) clear_has_trailing_comments(); return trailing_comments_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -14051,6 +14267,7 @@ void SourceCodeInfo_Location::clear_leading_detached_comments() { // @@protoc_insertion_point(field_set_pointer:google.protobuf.SourceCodeInfo.Location.leading_detached_comments) } ::std::string* SourceCodeInfo_Location::add_leading_detached_comments() { + // @@protoc_insertion_point(field_add_mutable:google.protobuf.SourceCodeInfo.Location.leading_detached_comments) return leading_detached_comments_.Add(); } void SourceCodeInfo_Location::add_leading_detached_comments(const ::std::string& value) { @@ -14184,8 +14401,17 @@ GeneratedCodeInfo_Annotation* GeneratedCodeInfo_Annotation::New(::google::protob } void GeneratedCodeInfo_Annotation::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.GeneratedCodeInfo.Annotation) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(GeneratedCodeInfo_Annotation, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -14211,7 +14437,7 @@ void GeneratedCodeInfo_Annotation::Clear() { bool GeneratedCodeInfo_Annotation::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.GeneratedCodeInfo.Annotation) for (;;) { @@ -14392,6 +14618,7 @@ void GeneratedCodeInfo_Annotation::SerializeWithCachedSizes( } int GeneratedCodeInfo_Annotation::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.GeneratedCodeInfo.Annotation) int total_size = 0; if (_has_bits_[1 / 32] & 14u) { @@ -14446,18 +14673,22 @@ int GeneratedCodeInfo_Annotation::ByteSize() const { } void GeneratedCodeInfo_Annotation::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.GeneratedCodeInfo.Annotation) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const GeneratedCodeInfo_Annotation* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.GeneratedCodeInfo.Annotation) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.GeneratedCodeInfo.Annotation) MergeFrom(*source); } } void GeneratedCodeInfo_Annotation::MergeFrom(const GeneratedCodeInfo_Annotation& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.GeneratedCodeInfo.Annotation) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); path_.MergeFrom(from.path_); if (from._has_bits_[1 / 32] & (0xffu << (1 % 32))) { @@ -14478,12 +14709,14 @@ void GeneratedCodeInfo_Annotation::MergeFrom(const GeneratedCodeInfo_Annotation& } void GeneratedCodeInfo_Annotation::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.GeneratedCodeInfo.Annotation) if (&from == this) return; Clear(); MergeFrom(from); } void GeneratedCodeInfo_Annotation::CopyFrom(const GeneratedCodeInfo_Annotation& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.GeneratedCodeInfo.Annotation) if (&from == this) return; Clear(); MergeFrom(from); @@ -14581,6 +14814,7 @@ GeneratedCodeInfo* GeneratedCodeInfo::New(::google::protobuf::Arena* arena) cons } void GeneratedCodeInfo::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.GeneratedCodeInfo) annotation_.Clear(); ::memset(_has_bits_, 0, sizeof(_has_bits_)); if (_internal_metadata_.have_unknown_fields()) { @@ -14590,7 +14824,7 @@ void GeneratedCodeInfo::Clear() { bool GeneratedCodeInfo::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.GeneratedCodeInfo) for (;;) { @@ -14671,6 +14905,7 @@ void GeneratedCodeInfo::SerializeWithCachedSizes( } int GeneratedCodeInfo::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.GeneratedCodeInfo) int total_size = 0; // repeated .google.protobuf.GeneratedCodeInfo.Annotation annotation = 1; @@ -14693,18 +14928,22 @@ int GeneratedCodeInfo::ByteSize() const { } void GeneratedCodeInfo::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.GeneratedCodeInfo) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const GeneratedCodeInfo* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.GeneratedCodeInfo) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.GeneratedCodeInfo) MergeFrom(*source); } } void GeneratedCodeInfo::MergeFrom(const GeneratedCodeInfo& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.GeneratedCodeInfo) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); annotation_.MergeFrom(from.annotation_); if (from._internal_metadata_.have_unknown_fields()) { @@ -14713,12 +14952,14 @@ void GeneratedCodeInfo::MergeFrom(const GeneratedCodeInfo& from) { } void GeneratedCodeInfo::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.GeneratedCodeInfo) if (&from == this) return; Clear(); MergeFrom(from); } void GeneratedCodeInfo::CopyFrom(const GeneratedCodeInfo& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.GeneratedCodeInfo) if (&from == this) return; Clear(); MergeFrom(from); @@ -14821,6 +15062,7 @@ void GeneratedCodeInfo_Annotation::clear_source_file() { return source_file_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* GeneratedCodeInfo_Annotation::release_source_file() { + // @@protoc_insertion_point(field_release:google.protobuf.GeneratedCodeInfo.Annotation.source_file) clear_has_source_file(); return source_file_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h index 3fe07bf5c..92a0a3a76 100644 --- a/src/google/protobuf/descriptor.pb.h +++ b/src/google/protobuf/descriptor.pb.h @@ -993,24 +993,42 @@ class LIBPROTOBUF_EXPORT FieldDescriptorProto : public ::google::protobuf::Messa // nested types ---------------------------------------------------- typedef FieldDescriptorProto_Type Type; - static const Type TYPE_DOUBLE = FieldDescriptorProto_Type_TYPE_DOUBLE; - static const Type TYPE_FLOAT = FieldDescriptorProto_Type_TYPE_FLOAT; - static const Type TYPE_INT64 = FieldDescriptorProto_Type_TYPE_INT64; - static const Type TYPE_UINT64 = FieldDescriptorProto_Type_TYPE_UINT64; - static const Type TYPE_INT32 = FieldDescriptorProto_Type_TYPE_INT32; - static const Type TYPE_FIXED64 = FieldDescriptorProto_Type_TYPE_FIXED64; - static const Type TYPE_FIXED32 = FieldDescriptorProto_Type_TYPE_FIXED32; - static const Type TYPE_BOOL = FieldDescriptorProto_Type_TYPE_BOOL; - static const Type TYPE_STRING = FieldDescriptorProto_Type_TYPE_STRING; - static const Type TYPE_GROUP = FieldDescriptorProto_Type_TYPE_GROUP; - static const Type TYPE_MESSAGE = FieldDescriptorProto_Type_TYPE_MESSAGE; - static const Type TYPE_BYTES = FieldDescriptorProto_Type_TYPE_BYTES; - static const Type TYPE_UINT32 = FieldDescriptorProto_Type_TYPE_UINT32; - static const Type TYPE_ENUM = FieldDescriptorProto_Type_TYPE_ENUM; - static const Type TYPE_SFIXED32 = FieldDescriptorProto_Type_TYPE_SFIXED32; - static const Type TYPE_SFIXED64 = FieldDescriptorProto_Type_TYPE_SFIXED64; - static const Type TYPE_SINT32 = FieldDescriptorProto_Type_TYPE_SINT32; - static const Type TYPE_SINT64 = FieldDescriptorProto_Type_TYPE_SINT64; + static const Type TYPE_DOUBLE = + FieldDescriptorProto_Type_TYPE_DOUBLE; + static const Type TYPE_FLOAT = + FieldDescriptorProto_Type_TYPE_FLOAT; + static const Type TYPE_INT64 = + FieldDescriptorProto_Type_TYPE_INT64; + static const Type TYPE_UINT64 = + FieldDescriptorProto_Type_TYPE_UINT64; + static const Type TYPE_INT32 = + FieldDescriptorProto_Type_TYPE_INT32; + static const Type TYPE_FIXED64 = + FieldDescriptorProto_Type_TYPE_FIXED64; + static const Type TYPE_FIXED32 = + FieldDescriptorProto_Type_TYPE_FIXED32; + static const Type TYPE_BOOL = + FieldDescriptorProto_Type_TYPE_BOOL; + static const Type TYPE_STRING = + FieldDescriptorProto_Type_TYPE_STRING; + static const Type TYPE_GROUP = + FieldDescriptorProto_Type_TYPE_GROUP; + static const Type TYPE_MESSAGE = + FieldDescriptorProto_Type_TYPE_MESSAGE; + static const Type TYPE_BYTES = + FieldDescriptorProto_Type_TYPE_BYTES; + static const Type TYPE_UINT32 = + FieldDescriptorProto_Type_TYPE_UINT32; + static const Type TYPE_ENUM = + FieldDescriptorProto_Type_TYPE_ENUM; + static const Type TYPE_SFIXED32 = + FieldDescriptorProto_Type_TYPE_SFIXED32; + static const Type TYPE_SFIXED64 = + FieldDescriptorProto_Type_TYPE_SFIXED64; + static const Type TYPE_SINT32 = + FieldDescriptorProto_Type_TYPE_SINT32; + static const Type TYPE_SINT64 = + FieldDescriptorProto_Type_TYPE_SINT64; static inline bool Type_IsValid(int value) { return FieldDescriptorProto_Type_IsValid(value); } @@ -1033,9 +1051,12 @@ class LIBPROTOBUF_EXPORT FieldDescriptorProto : public ::google::protobuf::Messa } typedef FieldDescriptorProto_Label Label; - static const Label LABEL_OPTIONAL = FieldDescriptorProto_Label_LABEL_OPTIONAL; - static const Label LABEL_REQUIRED = FieldDescriptorProto_Label_LABEL_REQUIRED; - static const Label LABEL_REPEATED = FieldDescriptorProto_Label_LABEL_REPEATED; + static const Label LABEL_OPTIONAL = + FieldDescriptorProto_Label_LABEL_OPTIONAL; + static const Label LABEL_REQUIRED = + FieldDescriptorProto_Label_LABEL_REQUIRED; + static const Label LABEL_REPEATED = + FieldDescriptorProto_Label_LABEL_REPEATED; static inline bool Label_IsValid(int value) { return FieldDescriptorProto_Label_IsValid(value); } @@ -1868,9 +1889,12 @@ class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message { // nested types ---------------------------------------------------- typedef FileOptions_OptimizeMode OptimizeMode; - static const OptimizeMode SPEED = FileOptions_OptimizeMode_SPEED; - static const OptimizeMode CODE_SIZE = FileOptions_OptimizeMode_CODE_SIZE; - static const OptimizeMode LITE_RUNTIME = FileOptions_OptimizeMode_LITE_RUNTIME; + static const OptimizeMode SPEED = + FileOptions_OptimizeMode_SPEED; + static const OptimizeMode CODE_SIZE = + FileOptions_OptimizeMode_CODE_SIZE; + static const OptimizeMode LITE_RUNTIME = + FileOptions_OptimizeMode_LITE_RUNTIME; static inline bool OptimizeMode_IsValid(int value) { return FileOptions_OptimizeMode_IsValid(value); } @@ -2017,13 +2041,6 @@ class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message { ::std::string* release_csharp_namespace(); void set_allocated_csharp_namespace(::std::string* csharp_namespace); - // optional bool javanano_use_deprecated_package = 38 [deprecated = true]; - bool has_javanano_use_deprecated_package() const PROTOBUF_DEPRECATED; - void clear_javanano_use_deprecated_package() PROTOBUF_DEPRECATED; - static const int kJavananoUseDeprecatedPackageFieldNumber = 38; - bool javanano_use_deprecated_package() const PROTOBUF_DEPRECATED; - void set_javanano_use_deprecated_package(bool value) PROTOBUF_DEPRECATED; - // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; int uninterpreted_option_size() const; void clear_uninterpreted_option(); @@ -2067,8 +2084,6 @@ class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message { inline void clear_has_objc_class_prefix(); inline void set_has_csharp_namespace(); inline void clear_has_csharp_namespace(); - inline void set_has_javanano_use_deprecated_package(); - inline void clear_has_javanano_use_deprecated_package(); ::google::protobuf::internal::ExtensionSet _extensions_; @@ -2083,14 +2098,13 @@ class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message { bool cc_generic_services_; int optimize_for_; ::google::protobuf::internal::ArenaStringPtr go_package_; + ::google::protobuf::internal::ArenaStringPtr objc_class_prefix_; + ::google::protobuf::internal::ArenaStringPtr csharp_namespace_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; bool java_generic_services_; bool py_generic_services_; bool deprecated_; bool cc_enable_arenas_; - bool javanano_use_deprecated_package_; - ::google::protobuf::internal::ArenaStringPtr objc_class_prefix_; - ::google::protobuf::internal::ArenaStringPtr csharp_namespace_; - ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); @@ -2298,9 +2312,12 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { // nested types ---------------------------------------------------- typedef FieldOptions_CType CType; - static const CType STRING = FieldOptions_CType_STRING; - static const CType CORD = FieldOptions_CType_CORD; - static const CType STRING_PIECE = FieldOptions_CType_STRING_PIECE; + static const CType STRING = + FieldOptions_CType_STRING; + static const CType CORD = + FieldOptions_CType_CORD; + static const CType STRING_PIECE = + FieldOptions_CType_STRING_PIECE; static inline bool CType_IsValid(int value) { return FieldOptions_CType_IsValid(value); } @@ -2323,9 +2340,12 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { } typedef FieldOptions_JSType JSType; - static const JSType JS_NORMAL = FieldOptions_JSType_JS_NORMAL; - static const JSType JS_STRING = FieldOptions_JSType_JS_STRING; - static const JSType JS_NUMBER = FieldOptions_JSType_JS_NUMBER; + static const JSType JS_NORMAL = + FieldOptions_JSType_JS_NORMAL; + static const JSType JS_STRING = + FieldOptions_JSType_JS_STRING; + static const JSType JS_NUMBER = + FieldOptions_JSType_JS_NUMBER; static inline bool JSType_IsValid(int value) { return FieldOptions_JSType_IsValid(value); } @@ -3696,6 +3716,7 @@ inline ::std::string* FileDescriptorProto::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* FileDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -3749,6 +3770,7 @@ inline ::std::string* FileDescriptorProto::mutable_package() { return package_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* FileDescriptorProto::release_package() { + // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.package) clear_has_package(); return package_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -3791,6 +3813,7 @@ inline void FileDescriptorProto::set_dependency(int index, const char* value, si // @@protoc_insertion_point(field_set_pointer:google.protobuf.FileDescriptorProto.dependency) } inline ::std::string* FileDescriptorProto::add_dependency() { + // @@protoc_insertion_point(field_add_mutable:google.protobuf.FileDescriptorProto.dependency) return dependency_.Add(); } inline void FileDescriptorProto::add_dependency(const ::std::string& value) { @@ -4023,6 +4046,7 @@ inline ::google::protobuf::FileOptions* FileDescriptorProto::mutable_options() { return options_; } inline ::google::protobuf::FileOptions* FileDescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.options) clear_has_options(); ::google::protobuf::FileOptions* temp = options_; options_ = NULL; @@ -4066,6 +4090,7 @@ inline ::google::protobuf::SourceCodeInfo* FileDescriptorProto::mutable_source_c return source_code_info_; } inline ::google::protobuf::SourceCodeInfo* FileDescriptorProto::release_source_code_info() { + // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.source_code_info) clear_has_source_code_info(); ::google::protobuf::SourceCodeInfo* temp = source_code_info_; source_code_info_ = NULL; @@ -4122,6 +4147,7 @@ inline ::std::string* FileDescriptorProto::mutable_syntax() { return syntax_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* FileDescriptorProto::release_syntax() { + // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.syntax) clear_has_syntax(); return syntax_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -4283,6 +4309,7 @@ inline ::std::string* DescriptorProto::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* DescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.DescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -4503,6 +4530,7 @@ inline ::google::protobuf::MessageOptions* DescriptorProto::mutable_options() { return options_; } inline ::google::protobuf::MessageOptions* DescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.DescriptorProto.options) clear_has_options(); ::google::protobuf::MessageOptions* temp = options_; options_ = NULL; @@ -4578,6 +4606,7 @@ inline void DescriptorProto::set_reserved_name(int index, const char* value, siz // @@protoc_insertion_point(field_set_pointer:google.protobuf.DescriptorProto.reserved_name) } inline ::std::string* DescriptorProto::add_reserved_name() { + // @@protoc_insertion_point(field_add_mutable:google.protobuf.DescriptorProto.reserved_name) return reserved_name_.Add(); } inline void DescriptorProto::add_reserved_name(const ::std::string& value) { @@ -4647,6 +4676,7 @@ inline ::std::string* FieldDescriptorProto::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* FieldDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -4774,6 +4804,7 @@ inline ::std::string* FieldDescriptorProto::mutable_type_name() { return type_name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* FieldDescriptorProto::release_type_name() { + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.type_name) clear_has_type_name(); return type_name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -4827,6 +4858,7 @@ inline ::std::string* FieldDescriptorProto::mutable_extendee() { return extendee_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* FieldDescriptorProto::release_extendee() { + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.extendee) clear_has_extendee(); return extendee_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -4880,6 +4912,7 @@ inline ::std::string* FieldDescriptorProto::mutable_default_value() { return default_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* FieldDescriptorProto::release_default_value() { + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.default_value) clear_has_default_value(); return default_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -4957,6 +4990,7 @@ inline ::std::string* FieldDescriptorProto::mutable_json_name() { return json_name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* FieldDescriptorProto::release_json_name() { + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.json_name) clear_has_json_name(); return json_name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -4997,6 +5031,7 @@ inline ::google::protobuf::FieldOptions* FieldDescriptorProto::mutable_options() return options_; } inline ::google::protobuf::FieldOptions* FieldDescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.options) clear_has_options(); ::google::protobuf::FieldOptions* temp = options_; options_ = NULL; @@ -5057,6 +5092,7 @@ inline ::std::string* OneofDescriptorProto::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* OneofDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.OneofDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5114,6 +5150,7 @@ inline ::std::string* EnumDescriptorProto::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* EnumDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.EnumDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5184,6 +5221,7 @@ inline ::google::protobuf::EnumOptions* EnumDescriptorProto::mutable_options() { return options_; } inline ::google::protobuf::EnumOptions* EnumDescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.EnumDescriptorProto.options) clear_has_options(); ::google::protobuf::EnumOptions* temp = options_; options_ = NULL; @@ -5244,6 +5282,7 @@ inline ::std::string* EnumValueDescriptorProto::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* EnumValueDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.EnumValueDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5308,6 +5347,7 @@ inline ::google::protobuf::EnumValueOptions* EnumValueDescriptorProto::mutable_o return options_; } inline ::google::protobuf::EnumValueOptions* EnumValueDescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.EnumValueDescriptorProto.options) clear_has_options(); ::google::protobuf::EnumValueOptions* temp = options_; options_ = NULL; @@ -5368,6 +5408,7 @@ inline ::std::string* ServiceDescriptorProto::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* ServiceDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.ServiceDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5438,6 +5479,7 @@ inline ::google::protobuf::ServiceOptions* ServiceDescriptorProto::mutable_optio return options_; } inline ::google::protobuf::ServiceOptions* ServiceDescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.ServiceDescriptorProto.options) clear_has_options(); ::google::protobuf::ServiceOptions* temp = options_; options_ = NULL; @@ -5498,6 +5540,7 @@ inline ::std::string* MethodDescriptorProto::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* MethodDescriptorProto::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.name) clear_has_name(); return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5551,6 +5594,7 @@ inline ::std::string* MethodDescriptorProto::mutable_input_type() { return input_type_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* MethodDescriptorProto::release_input_type() { + // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.input_type) clear_has_input_type(); return input_type_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5604,6 +5648,7 @@ inline ::std::string* MethodDescriptorProto::mutable_output_type() { return output_type_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* MethodDescriptorProto::release_output_type() { + // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.output_type) clear_has_output_type(); return output_type_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5644,6 +5689,7 @@ inline ::google::protobuf::MethodOptions* MethodDescriptorProto::mutable_options return options_; } inline ::google::protobuf::MethodOptions* MethodDescriptorProto::release_options() { + // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.options) clear_has_options(); ::google::protobuf::MethodOptions* temp = options_; options_ = NULL; @@ -5752,6 +5798,7 @@ inline ::std::string* FileOptions::mutable_java_package() { return java_package_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* FileOptions::release_java_package() { + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.java_package) clear_has_java_package(); return java_package_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5805,6 +5852,7 @@ inline ::std::string* FileOptions::mutable_java_outer_classname() { return java_outer_classname_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* FileOptions::release_java_outer_classname() { + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.java_outer_classname) clear_has_java_outer_classname(); return java_outer_classname_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -5955,6 +6003,7 @@ inline ::std::string* FileOptions::mutable_go_package() { return go_package_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* FileOptions::release_go_package() { + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.go_package) clear_has_go_package(); return go_package_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -6128,6 +6177,7 @@ inline ::std::string* FileOptions::mutable_objc_class_prefix() { return objc_class_prefix_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* FileOptions::release_objc_class_prefix() { + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.objc_class_prefix) clear_has_objc_class_prefix(); return objc_class_prefix_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -6181,6 +6231,7 @@ inline ::std::string* FileOptions::mutable_csharp_namespace() { return csharp_namespace_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* FileOptions::release_csharp_namespace() { + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.csharp_namespace) clear_has_csharp_namespace(); return csharp_namespace_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -6194,30 +6245,6 @@ inline void FileOptions::set_allocated_csharp_namespace(::std::string* csharp_na // @@protoc_insertion_point(field_set_allocated:google.protobuf.FileOptions.csharp_namespace) } -// optional bool javanano_use_deprecated_package = 38 [deprecated = true]; -inline bool FileOptions::has_javanano_use_deprecated_package() const { - return (_has_bits_[0] & 0x00004000u) != 0; -} -inline void FileOptions::set_has_javanano_use_deprecated_package() { - _has_bits_[0] |= 0x00004000u; -} -inline void FileOptions::clear_has_javanano_use_deprecated_package() { - _has_bits_[0] &= ~0x00004000u; -} -inline void FileOptions::clear_javanano_use_deprecated_package() { - javanano_use_deprecated_package_ = false; - clear_has_javanano_use_deprecated_package(); -} -inline bool FileOptions::javanano_use_deprecated_package() const { - // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.javanano_use_deprecated_package) - return javanano_use_deprecated_package_; -} -inline void FileOptions::set_javanano_use_deprecated_package(bool value) { - set_has_javanano_use_deprecated_package(); - javanano_use_deprecated_package_ = value; - // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.javanano_use_deprecated_package) -} - // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; inline int FileOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); @@ -6858,6 +6885,7 @@ inline ::std::string* UninterpretedOption_NamePart::mutable_name_part() { return name_part_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* UninterpretedOption_NamePart::release_name_part() { + // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.NamePart.name_part) clear_has_name_part(); return name_part_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -6969,6 +6997,7 @@ inline ::std::string* UninterpretedOption::mutable_identifier_value() { return identifier_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* UninterpretedOption::release_identifier_value() { + // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.identifier_value) clear_has_identifier_value(); return identifier_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -7094,6 +7123,7 @@ inline ::std::string* UninterpretedOption::mutable_string_value() { return string_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* UninterpretedOption::release_string_value() { + // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.string_value) clear_has_string_value(); return string_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -7147,6 +7177,7 @@ inline ::std::string* UninterpretedOption::mutable_aggregate_value() { return aggregate_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* UninterpretedOption::release_aggregate_value() { + // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.aggregate_value) clear_has_aggregate_value(); return aggregate_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -7264,6 +7295,7 @@ inline ::std::string* SourceCodeInfo_Location::mutable_leading_comments() { return leading_comments_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* SourceCodeInfo_Location::release_leading_comments() { + // @@protoc_insertion_point(field_release:google.protobuf.SourceCodeInfo.Location.leading_comments) clear_has_leading_comments(); return leading_comments_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -7317,6 +7349,7 @@ inline ::std::string* SourceCodeInfo_Location::mutable_trailing_comments() { return trailing_comments_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* SourceCodeInfo_Location::release_trailing_comments() { + // @@protoc_insertion_point(field_release:google.protobuf.SourceCodeInfo.Location.trailing_comments) clear_has_trailing_comments(); return trailing_comments_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -7359,6 +7392,7 @@ inline void SourceCodeInfo_Location::set_leading_detached_comments(int index, co // @@protoc_insertion_point(field_set_pointer:google.protobuf.SourceCodeInfo.Location.leading_detached_comments) } inline ::std::string* SourceCodeInfo_Location::add_leading_detached_comments() { + // @@protoc_insertion_point(field_add_mutable:google.protobuf.SourceCodeInfo.Location.leading_detached_comments) return leading_detached_comments_.Add(); } inline void SourceCodeInfo_Location::add_leading_detached_comments(const ::std::string& value) { @@ -7492,6 +7526,7 @@ inline ::std::string* GeneratedCodeInfo_Annotation::mutable_source_file() { return source_file_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* GeneratedCodeInfo_Annotation::release_source_file() { + // @@protoc_insertion_point(field_release:google.protobuf.GeneratedCodeInfo.Annotation.source_file) clear_has_source_file(); return source_file_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } diff --git a/src/google/protobuf/descriptor.proto b/src/google/protobuf/descriptor.proto index 3e664d59e..08b155548 100644 --- a/src/google/protobuf/descriptor.proto +++ b/src/google/protobuf/descriptor.proto @@ -377,15 +377,13 @@ message FileOptions { // Namespace for generated classes; defaults to the package. optional string csharp_namespace = 37; - // Whether the nano proto compiler should generate in the deprecated non-nano - // suffixed package. - optional bool javanano_use_deprecated_package = 38 [deprecated = true]; - // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; // Clients can define custom options in extensions of this message. See above. extensions 1000 to max; + + reserved 38; } message MessageOptions { diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc index be8e0b720..f937b9eac 100644 --- a/src/google/protobuf/descriptor_unittest.cc +++ b/src/google/protobuf/descriptor_unittest.cc @@ -54,6 +54,7 @@ #include #include +#include #include #include #include @@ -5739,18 +5740,6 @@ TEST_F(ValidationErrorTest, ValidateProto3Enum) { "}"); } -TEST_F(ValidationErrorTest, ValidateProto3LiteRuntime) { - // Lite runtime is not supported in proto3. - BuildFileWithErrors( - "name: 'foo.proto' " - "syntax: 'proto3' " - "options { " - " optimize_for: LITE_RUNTIME " - "} ", - "foo.proto: foo.proto: OTHER: Lite runtime is not supported " - "in proto3.\n"); -} - TEST_F(ValidationErrorTest, ValidateProto3Group) { BuildFileWithErrors( "name: 'foo.proto' " diff --git a/src/google/protobuf/duration.pb.cc b/src/google/protobuf/duration.pb.cc index b325944e7..e3639346d 100644 --- a/src/google/protobuf/duration.pb.cc +++ b/src/google/protobuf/duration.pb.cc @@ -83,9 +83,10 @@ void protobuf_AddDesc_google_2fprotobuf_2fduration_2eproto() { ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( "\n\036google/protobuf/duration.proto\022\017google" ".protobuf\"*\n\010Duration\022\017\n\007seconds\030\001 \001(\003\022\r" - "\n\005nanos\030\002 \001(\005BP\n\023com.google.protobufB\rDu" - "rationProtoP\001\240\001\001\242\002\003GPB\252\002\036Google.Protobuf" - ".WellKnownTypesb\006proto3", 183); + "\n\005nanos\030\002 \001(\005B|\n\023com.google.protobufB\rDu" + "rationProtoP\001Z*github.com/golang/protobu" + "f/ptypes/duration\240\001\001\242\002\003GPB\252\002\036Google.Prot" + "obuf.WellKnownTypesb\006proto3", 227); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/duration.proto", &protobuf_RegisterTypes); Duration::default_instance_ = new Duration(); @@ -178,8 +179,17 @@ Duration* Duration::New(::google::protobuf::Arena* arena) const { } void Duration::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Duration) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(Duration, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -195,7 +205,7 @@ void Duration::Clear() { bool Duration::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Duration) for (;;) { @@ -287,6 +297,7 @@ void Duration::SerializeWithCachedSizes( } int Duration::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Duration) int total_size = 0; // optional int64 seconds = 1; @@ -310,18 +321,22 @@ int Duration::ByteSize() const { } void Duration::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Duration) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Duration* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Duration) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Duration) MergeFrom(*source); } } void Duration::MergeFrom(const Duration& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Duration) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.seconds() != 0) { set_seconds(from.seconds()); @@ -332,12 +347,14 @@ void Duration::MergeFrom(const Duration& from) { } void Duration::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Duration) if (&from == this) return; Clear(); MergeFrom(from); } void Duration::CopyFrom(const Duration& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Duration) if (&from == this) return; Clear(); MergeFrom(from); diff --git a/src/google/protobuf/duration.proto b/src/google/protobuf/duration.proto index 78bcc74bb..96c1796d6 100644 --- a/src/google/protobuf/duration.proto +++ b/src/google/protobuf/duration.proto @@ -33,6 +33,7 @@ syntax = "proto3"; package google.protobuf; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/duration"; option java_package = "com.google.protobuf"; option java_outer_classname = "DurationProto"; option java_multiple_files = true; @@ -80,6 +81,7 @@ option objc_class_prefix = "GPB"; // end.nanos -= 1000000000; // } // +// message Duration { // Signed seconds of the span of time. Must be from -315,576,000,000 diff --git a/src/google/protobuf/dynamic_message.cc b/src/google/protobuf/dynamic_message.cc index 8d689ac88..9e83bd291 100644 --- a/src/google/protobuf/dynamic_message.cc +++ b/src/google/protobuf/dynamic_message.cc @@ -267,6 +267,16 @@ class DynamicMessage : public Message { Metadata GetMetadata() const; + // We actually allocate more memory than sizeof(*this) when this + // class's memory is allocated via the global operator new. Thus, we need to + // manually call the global operator delete. Calling the destructor is taken + // care of for us. This makes DynamicMessage compatible with -fsized-delete. + // It doesn't work for MSVC though. +#ifndef _MSC_VER + static void operator delete(void* ptr) { + ::operator delete(ptr); + } +#endif // !_MSC_VER private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DynamicMessage); @@ -442,8 +452,10 @@ DynamicMessage::~DynamicMessage() { case FieldOptions::STRING: { const ::std::string* default_value = &(reinterpret_cast( - type_info_->prototype->OffsetToPointer( - type_info_->offsets[i]))->Get(NULL)); + reinterpret_cast( + type_info_->default_oneof_instance) + + type_info_->offsets[i]) + ->Get(NULL)); reinterpret_cast(field_ptr)->Destroy( default_value, NULL); break; @@ -704,7 +716,7 @@ const Message* DynamicMessageFactory::GetPrototypeNoLock( // Oneof fields do not use any space. if (!type->field(i)->containing_oneof()) { int field_size = FieldSpaceUsed(type->field(i)); - size = AlignTo(size, min(kSafeAlignment, field_size)); + size = AlignTo(size, std::min(kSafeAlignment, field_size)); offsets[i] = size; size += field_size; } @@ -748,7 +760,7 @@ const Message* DynamicMessageFactory::GetPrototypeNoLock( for (int j = 0; j < type->oneof_decl(i)->field_count(); j++) { const FieldDescriptor* field = type->oneof_decl(i)->field(j); int field_size = OneofFieldSpaceUsed(field); - oneof_size = AlignTo(oneof_size, min(kSafeAlignment, field_size)); + oneof_size = AlignTo(oneof_size, std::min(kSafeAlignment, field_size)); offsets[field->index()] = oneof_size; oneof_size += field_size; } diff --git a/src/google/protobuf/empty.pb.cc b/src/google/protobuf/empty.pb.cc index f2eec7823..dcf842631 100644 --- a/src/google/protobuf/empty.pb.cc +++ b/src/google/protobuf/empty.pb.cc @@ -80,9 +80,10 @@ void protobuf_AddDesc_google_2fprotobuf_2fempty_2eproto() { ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( "\n\033google/protobuf/empty.proto\022\017google.pr" - "otobuf\"\007\n\005EmptyBP\n\023com.google.protobufB\n" - "EmptyProtoP\001\240\001\001\370\001\001\242\002\003GPB\252\002\036Google.Protob" - "uf.WellKnownTypesb\006proto3", 145); + "otobuf\"\007\n\005EmptyBy\n\023com.google.protobufB\n" + "EmptyProtoP\001Z\'github.com/golang/protobuf" + "/ptypes/empty\240\001\001\370\001\001\242\002\003GPB\252\002\036Google.Proto" + "buf.WellKnownTypesb\006proto3", 186); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/empty.proto", &protobuf_RegisterTypes); Empty::default_instance_ = new Empty(); @@ -185,11 +186,12 @@ Empty* Empty::New(::google::protobuf::Arena* arena) const { } void Empty::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Empty) } bool Empty::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Empty) for (;;) { @@ -227,6 +229,7 @@ void Empty::SerializeWithCachedSizes( } int Empty::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Empty) int total_size = 0; GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); @@ -236,28 +239,34 @@ int Empty::ByteSize() const { } void Empty::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Empty) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Empty* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Empty) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Empty) MergeFrom(*source); } } void Empty::MergeFrom(const Empty& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Empty) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); } void Empty::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Empty) if (&from == this) return; Clear(); MergeFrom(from); } void Empty::CopyFrom(const Empty& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Empty) if (&from == this) return; Clear(); MergeFrom(from); diff --git a/src/google/protobuf/empty.proto b/src/google/protobuf/empty.proto index b96daf280..37f4cd10e 100644 --- a/src/google/protobuf/empty.proto +++ b/src/google/protobuf/empty.proto @@ -33,6 +33,7 @@ syntax = "proto3"; package google.protobuf; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/empty"; option java_package = "com.google.protobuf"; option java_outer_classname = "EmptyProto"; option java_multiple_files = true; diff --git a/src/google/protobuf/field_mask.pb.cc b/src/google/protobuf/field_mask.pb.cc index 01a6ce56d..c49ebceb5 100644 --- a/src/google/protobuf/field_mask.pb.cc +++ b/src/google/protobuf/field_mask.pb.cc @@ -175,12 +175,13 @@ FieldMask* FieldMask::New(::google::protobuf::Arena* arena) const { } void FieldMask::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.FieldMask) paths_.Clear(); } bool FieldMask::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.FieldMask) for (;;) { @@ -262,6 +263,7 @@ void FieldMask::SerializeWithCachedSizes( } int FieldMask::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FieldMask) int total_size = 0; // repeated string paths = 1; @@ -278,29 +280,35 @@ int FieldMask::ByteSize() const { } void FieldMask::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.FieldMask) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const FieldMask* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.FieldMask) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.FieldMask) MergeFrom(*source); } } void FieldMask::MergeFrom(const FieldMask& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FieldMask) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); paths_.MergeFrom(from.paths_); } void FieldMask::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.FieldMask) if (&from == this) return; Clear(); MergeFrom(from); } void FieldMask::CopyFrom(const FieldMask& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FieldMask) if (&from == this) return; Clear(); MergeFrom(from); @@ -361,6 +369,7 @@ void FieldMask::clear_paths() { // @@protoc_insertion_point(field_set_pointer:google.protobuf.FieldMask.paths) } ::std::string* FieldMask::add_paths() { + // @@protoc_insertion_point(field_add_mutable:google.protobuf.FieldMask.paths) return paths_.Add(); } void FieldMask::add_paths(const ::std::string& value) { diff --git a/src/google/protobuf/field_mask.pb.h b/src/google/protobuf/field_mask.pb.h index 7189fd791..f5e0b6553 100644 --- a/src/google/protobuf/field_mask.pb.h +++ b/src/google/protobuf/field_mask.pb.h @@ -164,6 +164,7 @@ inline void FieldMask::set_paths(int index, const char* value, size_t size) { // @@protoc_insertion_point(field_set_pointer:google.protobuf.FieldMask.paths) } inline ::std::string* FieldMask::add_paths() { + // @@protoc_insertion_point(field_add_mutable:google.protobuf.FieldMask.paths) return paths_.Add(); } inline void FieldMask::add_paths(const ::std::string& value) { diff --git a/src/google/protobuf/field_mask.proto b/src/google/protobuf/field_mask.proto index 908c8a86d..6af6dbe89 100644 --- a/src/google/protobuf/field_mask.proto +++ b/src/google/protobuf/field_mask.proto @@ -88,7 +88,7 @@ option java_generate_equals_and_hash = true; // operation applies to all fields (as if a FieldMask of all fields // had been specified). // -// Note that a field mask does not necessarily applies to the +// Note that a field mask does not necessarily apply to the // top-level response message. In case of a REST get operation, the // field mask applies directly to the response, but in case of a REST // list operation, the mask instead applies to each individual message @@ -162,6 +162,32 @@ option java_generate_equals_and_hash = true; // mask: "user.displayName,photo" // } // +// # Field Masks and Oneof Fields +// +// Field masks treat fields in oneofs just as regular fields. Consider the +// following message: +// +// message SampleMessage { +// oneof test_oneof { +// string name = 4; +// SubMessage sub_message = 9; +// } +// } +// +// The field mask can be: +// +// mask { +// paths: "name" +// } +// +// Or: +// +// mask { +// paths: "sub_message" +// } +// +// Note that oneof type names ("test_oneof" in this case) cannot be used in +// paths. message FieldMask { // The set of field mask paths. repeated string paths = 1; diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc index eee024ee0..2313181cc 100644 --- a/src/google/protobuf/generated_message_reflection.cc +++ b/src/google/protobuf/generated_message_reflection.cc @@ -412,6 +412,15 @@ void GeneratedMessageReflection::SwapField( #undef SWAP_ARRAYS case FieldDescriptor::CPPTYPE_STRING: + switch (field->options().ctype()) { + default: // TODO(kenton): Support other string reps. + case FieldOptions::STRING: + MutableRaw(message1, field)-> + Swap >( + MutableRaw(message2, field)); + break; + } + break; case FieldDescriptor::CPPTYPE_MESSAGE: if (IsMapFieldInApi(field)) { MutableRaw(message1, field)-> @@ -447,16 +456,50 @@ void GeneratedMessageReflection::SwapField( SWAP_VALUES(ENUM , int ); #undef SWAP_VALUES case FieldDescriptor::CPPTYPE_MESSAGE: - std::swap(*MutableRaw(message1, field), - *MutableRaw(message2, field)); + if (GetArena(message1) == GetArena(message2)) { + std::swap(*MutableRaw(message1, field), + *MutableRaw(message2, field)); + } else { + Message** sub_msg1 = MutableRaw(message1, field); + Message** sub_msg2 = MutableRaw(message2, field); + if (*sub_msg1 == NULL && *sub_msg2 == NULL) break; + if (*sub_msg1 && *sub_msg2) { + (*sub_msg1)->GetReflection()->Swap(*sub_msg1, *sub_msg2); + break; + } + if (*sub_msg1 == NULL) { + *sub_msg1 = (*sub_msg2)->New(message1->GetArena()); + (*sub_msg1)->CopyFrom(**sub_msg2); + ClearField(message2, field); + } else { + *sub_msg2 = (*sub_msg1)->New(message2->GetArena()); + (*sub_msg2)->CopyFrom(**sub_msg1); + ClearField(message1, field); + } + } break; case FieldDescriptor::CPPTYPE_STRING: switch (field->options().ctype()) { default: // TODO(kenton): Support other string reps. case FieldOptions::STRING: - MutableRaw(message1, field)->Swap( - MutableRaw(message2, field)); + { + Arena* arena1 = GetArena(message1); + Arena* arena2 = GetArena(message2); + ArenaStringPtr* string1 = + MutableRaw(message1, field); + ArenaStringPtr* string2 = + MutableRaw(message2, field); + if (arena1 == arena2) { + string1->Swap(string2); + } else { + const string* default_ptr = + &DefaultRaw(field).Get(NULL); + const string temp = string1->Get(default_ptr); + string1->Set(default_ptr, string2->Get(default_ptr), arena1); + string2->Set(default_ptr, temp, arena2); + } + } break; } break; @@ -1752,7 +1795,8 @@ bool GeneratedMessageReflection::InsertOrLookupMapValue( "InsertOrLookupMapValue", "Field is not a map field."); val->SetType(field->message_type()->FindFieldByName("value")->cpp_type()); - return MutableRaw(message, field)->InsertMapValue(key, val); + return MutableRaw(message, field)->InsertOrLookupMapValue( + key, val); } bool GeneratedMessageReflection::DeleteMapValue( diff --git a/src/google/protobuf/generated_message_reflection.h b/src/google/protobuf/generated_message_reflection.h index 9ef787107..15fc802ce 100644 --- a/src/google/protobuf/generated_message_reflection.h +++ b/src/google/protobuf/generated_message_reflection.h @@ -582,7 +582,16 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { // which the offsets of the direct fields of a class are non-constant. // Fields inherited from superclasses *can* have non-constant offsets, // but that's not what this macro will be used for. -// +#if defined(__clang__) +// For Clang we use __builtin_offsetof() and suppress the warning, +// to avoid Control Flow Integrity and UBSan vptr sanitizers from +// crashing while trying to validate the invalid reinterpet_casts. +#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(TYPE, FIELD) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(TYPE, FIELD) \ + _Pragma("clang diagnostic pop") +#else // Note that we calculate relative to the pointer value 16 here since if we // just use zero, GCC complains about dereferencing a NULL pointer. We // choose 16 rather than some other number just in case the compiler would @@ -592,6 +601,7 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { reinterpret_cast( \ &reinterpret_cast(16)->FIELD) - \ reinterpret_cast(16)) +#endif #define PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(ONEOF, FIELD) \ static_cast( \ diff --git a/src/google/protobuf/generated_message_util.h b/src/google/protobuf/generated_message_util.h index 78c8d7ff4..4475556ab 100644 --- a/src/google/protobuf/generated_message_util.h +++ b/src/google/protobuf/generated_message_util.h @@ -42,8 +42,8 @@ #include #include - #include + namespace google { namespace protobuf { @@ -63,6 +63,8 @@ namespace internal { #undef DEPRECATED_PROTOBUF_FIELD #define PROTOBUF_DEPRECATED +#define PROTOBUF_DEPRECATED_ATTR + // Constants for special floating point values. LIBPROTOBUF_EXPORT double Infinity(); diff --git a/src/google/protobuf/io/coded_stream.cc b/src/google/protobuf/io/coded_stream.cc index e3a34d0ac..d8354c1f9 100644 --- a/src/google/protobuf/io/coded_stream.cc +++ b/src/google/protobuf/io/coded_stream.cc @@ -105,7 +105,7 @@ void CodedInputStream::BackUpInputToCurrentPosition() { inline void CodedInputStream::RecomputeBufferLimits() { buffer_end_ += buffer_size_after_limit_; - int closest_limit = min(current_limit_, total_bytes_limit_); + int closest_limit = std::min(current_limit_, total_bytes_limit_); if (closest_limit < total_bytes_read_) { // The limit position is in the current buffer. We must adjust // the buffer size accordingly. @@ -135,7 +135,7 @@ CodedInputStream::Limit CodedInputStream::PushLimit(int byte_limit) { // We need to enforce all limits, not just the new one, so if the previous // limit was before the new requested limit, we continue to enforce the // previous limit. - current_limit_ = min(current_limit_, old_limit); + current_limit_ = std::min(current_limit_, old_limit); RecomputeBufferLimits(); return old_limit; @@ -188,7 +188,7 @@ void CodedInputStream::SetTotalBytesLimit( // Make sure the limit isn't already past, since this could confuse other // code. int current_position = CurrentPosition(); - total_bytes_limit_ = max(current_position, total_bytes_limit); + total_bytes_limit_ = std::max(current_position, total_bytes_limit); if (warning_threshold >= 0) { total_bytes_warning_threshold_ = warning_threshold; } else { @@ -233,7 +233,7 @@ bool CodedInputStream::Skip(int count) { buffer_end_ = buffer_; // Make sure this skip doesn't try to skip past the current limit. - int closest_limit = min(current_limit_, total_bytes_limit_); + int closest_limit = std::min(current_limit_, total_bytes_limit_); int bytes_until_limit = closest_limit - total_bytes_read_; if (bytes_until_limit < count) { // We hit the limit. Skip up to it then fail. @@ -270,7 +270,7 @@ bool CodedInputStream::ReadStringFallback(string* buffer, int size) { buffer->clear(); } - int closest_limit = min(current_limit_, total_bytes_limit_); + int closest_limit = std::min(current_limit_, total_bytes_limit_); if (closest_limit != INT_MAX) { int bytes_to_limit = closest_limit - CurrentPosition(); if (bytes_to_limit > 0 && size > 0 && size <= bytes_to_limit) { diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h index e37710035..c81a33ac6 100644 --- a/src/google/protobuf/io/coded_stream.h +++ b/src/google/protobuf/io/coded_stream.h @@ -1136,7 +1136,7 @@ inline void CodedOutputStream::WriteVarint32(uint32 value) { // this write won't cross the end, so we can skip the checks. uint8* target = buffer_; uint8* end = WriteVarint32ToArray(value, target); - int size = end - target; + int size = static_cast(end - target); Advance(size); } else { WriteVarint32SlowPath(value); diff --git a/src/google/protobuf/io/coded_stream_unittest.cc b/src/google/protobuf/io/coded_stream_unittest.cc index d1782e39a..a8108e457 100644 --- a/src/google/protobuf/io/coded_stream_unittest.cc +++ b/src/google/protobuf/io/coded_stream_unittest.cc @@ -46,7 +46,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/google/protobuf/io/gzip_stream.h b/src/google/protobuf/io/gzip_stream.h index 824450001..15b02fe3d 100644 --- a/src/google/protobuf/io/gzip_stream.h +++ b/src/google/protobuf/io/gzip_stream.h @@ -43,10 +43,9 @@ #ifndef GOOGLE_PROTOBUF_IO_GZIP_STREAM_H__ #define GOOGLE_PROTOBUF_IO_GZIP_STREAM_H__ -#include - #include #include +#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/io/printer.cc b/src/google/protobuf/io/printer.cc index 7d8865062..7532b098b 100644 --- a/src/google/protobuf/io/printer.cc +++ b/src/google/protobuf/io/printer.cc @@ -42,13 +42,25 @@ namespace protobuf { namespace io { Printer::Printer(ZeroCopyOutputStream* output, char variable_delimiter) - : variable_delimiter_(variable_delimiter), - output_(output), - buffer_(NULL), - buffer_size_(0), - at_start_of_line_(true), - failed_(false) { -} + : variable_delimiter_(variable_delimiter), + output_(output), + buffer_(NULL), + buffer_size_(0), + offset_(0), + at_start_of_line_(true), + failed_(false), + annotation_collector_(NULL) {} + +Printer::Printer(ZeroCopyOutputStream* output, char variable_delimiter, + AnnotationCollector* annotation_collector) + : variable_delimiter_(variable_delimiter), + output_(output), + buffer_(NULL), + buffer_size_(0), + offset_(0), + at_start_of_line_(true), + failed_(false), + annotation_collector_(annotation_collector) {} Printer::~Printer() { // Only BackUp() if we have called Next() at least once and never failed. @@ -57,9 +69,47 @@ Printer::~Printer() { } } +bool Printer::GetSubstitutionRange(const char* varname, + pair* range) { + map >::const_iterator iter = + substitutions_.find(varname); + if (iter == substitutions_.end()) { + GOOGLE_LOG(DFATAL) << " Undefined variable in annotation: " << varname; + return false; + } + if (iter->second.first > iter->second.second) { + GOOGLE_LOG(DFATAL) << " Variable used for annotation used multiple times: " + << varname; + return false; + } + *range = iter->second; + return true; +} + +void Printer::Annotate(const char* begin_varname, const char* end_varname, + const string& file_path, const vector& path) { + if (annotation_collector_ == NULL) { + // Can't generate signatures with this Printer. + return; + } + pair begin, end; + if (!GetSubstitutionRange(begin_varname, &begin) || + !GetSubstitutionRange(end_varname, &end)) { + return; + } + if (begin.first > end.second) { + GOOGLE_LOG(DFATAL) << " Annotation has negative length from " << begin_varname + << " to " << end_varname; + } else { + annotation_collector_->AddAnnotation(begin.first, end.second, file_path, + path); + } +} + void Printer::Print(const map& variables, const char* text) { int size = strlen(text); int pos = 0; // The number of bytes we've written so far. + substitutions_.clear(); for (int i = 0; i < size; i++) { if (text[i] == '\n') { @@ -97,7 +147,17 @@ void Printer::Print(const map& variables, const char* text) { if (iter == variables.end()) { GOOGLE_LOG(DFATAL) << " Undefined variable: " << varname; } else { + size_t begin = offset_; WriteRaw(iter->second.data(), iter->second.size()); + pair >::iterator, bool> inserted = + substitutions_.insert( + std::make_pair(varname, std::make_pair(begin, offset_))); + if (!inserted.second) { + // This variable was used multiple times. Make its span have + // negative length so we can detect it if it gets used in an + // annotation. + inserted.first->second = std::make_pair(1, 0); + } } } @@ -265,6 +325,7 @@ void Printer::WriteRaw(const char* data, int size) { // Data exceeds space in the buffer. Copy what we can and request a // new buffer. memcpy(buffer_, data, buffer_size_); + offset_ += buffer_size_; data += buffer_size_; size -= buffer_size_; void* void_buffer; @@ -277,6 +338,7 @@ void Printer::WriteRaw(const char* data, int size) { memcpy(buffer_, data, size); buffer_ += size; buffer_size_ -= size; + offset_ += size; } } // namespace io diff --git a/src/google/protobuf/io/printer.h b/src/google/protobuf/io/printer.h index f1490bbe5..2ba845590 100644 --- a/src/google/protobuf/io/printer.h +++ b/src/google/protobuf/io/printer.h @@ -39,6 +39,7 @@ #include #include +#include #include namespace google { @@ -47,6 +48,47 @@ namespace io { class ZeroCopyOutputStream; // zero_copy_stream.h +// Records annotations about a Printer's output. +class LIBPROTOBUF_EXPORT AnnotationCollector { + public: + // Records that the bytes in file_path beginning with begin_offset and ending + // before end_offset are associated with the SourceCodeInfo-style path. + virtual void AddAnnotation(size_t begin_offset, size_t end_offset, + const string& file_path, + const vector& path) = 0; + + virtual ~AnnotationCollector() {} +}; + +// Records annotations about a Printer's output to the given protocol buffer, +// assuming that the buffer has an ::Annotation message exposing path, +// source_file, begin and end fields. +template +class AnnotationProtoCollector : public AnnotationCollector { + public: + // annotation_proto is the protocol buffer to which new Annotations should be + // added. It is not owned by the AnnotationProtoCollector. + explicit AnnotationProtoCollector(AnnotationProto* annotation_proto) + : annotation_proto_(annotation_proto) {} + + // Override for AnnotationCollector::AddAnnotation. + virtual void AddAnnotation(size_t begin_offset, size_t end_offset, + const string& file_path, const vector& path) { + typename AnnotationProto::Annotation* annotation = + annotation_proto_->add_annotation(); + for (int i = 0; i < path.size(); ++i) { + annotation->add_path(path[i]); + } + annotation->set_source_file(file_path); + annotation->set_begin(begin_offset); + annotation->set_end(end_offset); + } + + private: + // The protocol buffer to which new annotations should be added. + AnnotationProto* const annotation_proto_; +}; + // This simple utility class assists in code generation. It basically // allows the caller to define a set of variables and then output some // text with variable substitutions. Example usage: @@ -61,13 +103,103 @@ class ZeroCopyOutputStream; // zero_copy_stream.h // Printer aggressively enforces correct usage, crashing (with assert failures) // in the case of undefined variables in debug builds. This helps greatly in // debugging code which uses it. +// +// If a Printer is constructed with an AnnotationCollector, it will provide it +// with annotations that connect the Printer's output to paths that can identify +// various descriptors. In the above example, if person_ is a descriptor that +// identifies Bob, we can associate the output string "My name is Bob." with +// a source path pointing to that descriptor with: +// +// printer.Annotate("name", person_); +// +// The AnnotationCollector will be sent an annotation linking the output range +// covering "Bob" to the logical path provided by person_. Tools may use +// this association to (for example) link "Bob" in the output back to the +// source file that defined the person_ descriptor identifying Bob. +// +// Annotate can only examine variables substituted during the last call to +// Print. It is invalid to refer to a variable that was used multiple times +// in a single Print call. +// +// In full generality, one may specify a range of output text using a beginning +// substitution variable and an ending variable. The resulting annotation will +// span from the first character of the substituted value for the beginning +// variable to the last character of the substituted value for the ending +// variable. For example, the Annotate call above is equivalent to this one: +// +// printer.Annotate("name", "name", person_); +// +// This is useful if multiple variables combine to form a single span of output +// that should be annotated with the same source path. For example: +// +// Printer printer(output, '$'); +// map vars; +// vars["first"] = "Alice"; +// vars["last"] = "Smith"; +// printer.Print(vars, "My name is $first$ $last$."); +// printer.Annotate("first", "last", person_); +// +// This code would associate the span covering "Alice Smith" in the output with +// the person_ descriptor. +// +// Note that the beginning variable must come before (or overlap with, in the +// case of zero-sized substitution values) the ending variable. +// +// It is also sometimes useful to use variables with zero-sized values as +// markers. This avoids issues with multiple references to the same variable +// and also allows annotation ranges to span literal text from the Print +// templates: +// +// Printer printer(output, '$'); +// map vars; +// vars["foo"] = "bar"; +// vars["function"] = "call"; +// vars["mark"] = ""; +// printer.Print(vars, "$function$($foo$,$foo$)$mark$"); +// printer.Annotate("function", "rmark", call_); +// +// This code associates the span covering "call(bar,bar)" in the output with the +// call_ descriptor. + class LIBPROTOBUF_EXPORT Printer { public: // Create a printer that writes text to the given output stream. Use the // given character as the delimiter for variables. Printer(ZeroCopyOutputStream* output, char variable_delimiter); + + // Create a printer that writes text to the given output stream. Use the + // given character as the delimiter for variables. If annotation_collector + // is not null, Printer will provide it with annotations about code written + // to the stream. annotation_collector is not owned by Printer. + Printer(ZeroCopyOutputStream* output, char variable_delimiter, + AnnotationCollector* annotation_collector); + ~Printer(); + // Link a subsitution variable emitted by the last call to Print to the object + // described by descriptor. + template + void Annotate(const char* varname, const SomeDescriptor* descriptor) { + Annotate(varname, varname, descriptor); + } + + // Link the output range defined by the substitution variables as emitted by + // the last call to Print to the object described by descriptor. The range + // begins at begin_varname's value and ends after the last character of the + // value substituted for end_varname. + template + void Annotate(const char* begin_varname, const char* end_varname, + const SomeDescriptor* descriptor) { + if (annotation_collector_ == NULL) { + // Annotations aren't turned on for this Printer, so don't pay the cost + // of building the location path. + return; + } + vector path; + descriptor->GetLocationPath(&path); + Annotate(begin_varname, end_varname, descriptor->file()->name(), path); + } + // Print some text after applying variable substitutions. If a particular // variable in the text is not defined, this will crash. Variables to be // substituted are identified by their names surrounded by delimiter @@ -149,16 +281,48 @@ class LIBPROTOBUF_EXPORT Printer { bool failed() const { return failed_; } private: + // Link the output range defined by the substitution variables as emitted by + // the last call to Print to the object found at the SourceCodeInfo-style path + // in a file with path file_path. The range begins at the start of + // begin_varname's value and ends after the last character of the value + // substituted for end_varname. Note that begin_varname and end_varname + // may refer to the same variable. + void Annotate(const char* begin_varname, const char* end_varname, + const string& file_path, const vector& path); + const char variable_delimiter_; ZeroCopyOutputStream* const output_; char* buffer_; int buffer_size_; + // The current position, in bytes, in the output stream. This is equivalent + // to the total number of bytes that have been written so far. This value is + // used to calculate annotation ranges in the substitutions_ map below. + size_t offset_; string indent_; bool at_start_of_line_; bool failed_; + // A map from variable name to [start, end) offsets in the output buffer. + // These refer to the offsets used for a variable after the last call to + // Print. If a variable was used more than once, the entry used in + // this map is set to a negative-length span. For singly-used variables, the + // start offset is the beginning of the substitution; the end offset is the + // last byte of the substitution plus one (such that (end - start) is the + // length of the substituted string). + map > substitutions_; + + // Returns true and sets range to the substitution range in the output for + // varname if varname was used once in the last call to Print. If varname + // was not used, or if it was used multiple times, returns false (and + // fails a debug assertion). + bool GetSubstitutionRange(const char* varname, pair* range); + + // If non-null, annotation_collector_ is used to store annotations about + // generated code. + AnnotationCollector* const annotation_collector_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Printer); }; diff --git a/src/google/protobuf/io/printer_unittest.cc b/src/google/protobuf/io/printer_unittest.cc index 258dd9861..95f3afa21 100644 --- a/src/google/protobuf/io/printer_unittest.cc +++ b/src/google/protobuf/io/printer_unittest.cc @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -169,6 +170,196 @@ TEST(Printer, InlineVariableSubstitution) { buffer); } +// MockDescriptorFile defines only those members that Printer uses to write out +// annotations. +class MockDescriptorFile { + public: + explicit MockDescriptorFile(const string& file) : file_(file) {} + + // The mock filename for this file. + const string& name() const { return file_; } + + private: + string file_; +}; + +// MockDescriptor defines only those members that Printer uses to write out +// annotations. +class MockDescriptor { + public: + MockDescriptor(const string& file, const vector& path) + : file_(file), path_(path) {} + + // The mock file in which this descriptor was defined. + const MockDescriptorFile* file() const { return &file_; } + + private: + // Allows access to GetLocationPath. + friend class ::google::protobuf::io::Printer; + + // Copies the pre-stored path to output. + void GetLocationPath(std::vector* output) const { *output = path_; } + + MockDescriptorFile file_; + vector path_; +}; + +TEST(Printer, AnnotateMap) { + char buffer[8192]; + ArrayOutputStream output(buffer, sizeof(buffer)); + GeneratedCodeInfo info; + AnnotationProtoCollector info_collector(&info); + { + Printer printer(&output, '$', &info_collector); + map vars; + vars["foo"] = "3"; + vars["bar"] = "5"; + printer.Print(vars, "012$foo$4$bar$\n"); + vector path_1; + path_1.push_back(33); + vector path_2; + path_2.push_back(11); + path_2.push_back(22); + MockDescriptor descriptor_1("path_1", path_1); + MockDescriptor descriptor_2("path_2", path_2); + printer.Annotate("foo", "foo", &descriptor_1); + printer.Annotate("bar", "bar", &descriptor_2); + } + buffer[output.ByteCount()] = '\0'; + EXPECT_STREQ("012345\n", buffer); + ASSERT_EQ(2, info.annotation_size()); + const GeneratedCodeInfo::Annotation* foo = info.annotation(0).path_size() == 1 + ? &info.annotation(0) + : &info.annotation(1); + const GeneratedCodeInfo::Annotation* bar = info.annotation(0).path_size() == 1 + ? &info.annotation(1) + : &info.annotation(0); + ASSERT_EQ(1, foo->path_size()); + ASSERT_EQ(2, bar->path_size()); + EXPECT_EQ(33, foo->path(0)); + EXPECT_EQ(11, bar->path(0)); + EXPECT_EQ(22, bar->path(1)); + EXPECT_EQ("path_1", foo->source_file()); + EXPECT_EQ("path_2", bar->source_file()); + EXPECT_EQ(3, foo->begin()); + EXPECT_EQ(4, foo->end()); + EXPECT_EQ(5, bar->begin()); + EXPECT_EQ(6, bar->end()); +} + +TEST(Printer, AnnotateInline) { + char buffer[8192]; + ArrayOutputStream output(buffer, sizeof(buffer)); + GeneratedCodeInfo info; + AnnotationProtoCollector info_collector(&info); + { + Printer printer(&output, '$', &info_collector); + printer.Print("012$foo$4$bar$\n", "foo", "3", "bar", "5"); + vector path_1; + path_1.push_back(33); + vector path_2; + path_2.push_back(11); + path_2.push_back(22); + MockDescriptor descriptor_1("path_1", path_1); + MockDescriptor descriptor_2("path_2", path_2); + printer.Annotate("foo", "foo", &descriptor_1); + printer.Annotate("bar", "bar", &descriptor_2); + } + buffer[output.ByteCount()] = '\0'; + EXPECT_STREQ("012345\n", buffer); + ASSERT_EQ(2, info.annotation_size()); + const GeneratedCodeInfo::Annotation* foo = info.annotation(0).path_size() == 1 + ? &info.annotation(0) + : &info.annotation(1); + const GeneratedCodeInfo::Annotation* bar = info.annotation(0).path_size() == 1 + ? &info.annotation(1) + : &info.annotation(0); + ASSERT_EQ(1, foo->path_size()); + ASSERT_EQ(2, bar->path_size()); + EXPECT_EQ(33, foo->path(0)); + EXPECT_EQ(11, bar->path(0)); + EXPECT_EQ(22, bar->path(1)); + EXPECT_EQ("path_1", foo->source_file()); + EXPECT_EQ("path_2", bar->source_file()); + EXPECT_EQ(3, foo->begin()); + EXPECT_EQ(4, foo->end()); + EXPECT_EQ(5, bar->begin()); + EXPECT_EQ(6, bar->end()); +} + +TEST(Printer, AnnotateRange) { + char buffer[8192]; + ArrayOutputStream output(buffer, sizeof(buffer)); + GeneratedCodeInfo info; + AnnotationProtoCollector info_collector(&info); + { + Printer printer(&output, '$', &info_collector); + printer.Print("012$foo$4$bar$\n", "foo", "3", "bar", "5"); + vector path; + path.push_back(33); + MockDescriptor descriptor("path", path); + printer.Annotate("foo", "bar", &descriptor); + } + buffer[output.ByteCount()] = '\0'; + EXPECT_STREQ("012345\n", buffer); + ASSERT_EQ(1, info.annotation_size()); + const GeneratedCodeInfo::Annotation* foobar = &info.annotation(0); + ASSERT_EQ(1, foobar->path_size()); + EXPECT_EQ(33, foobar->path(0)); + EXPECT_EQ("path", foobar->source_file()); + EXPECT_EQ(3, foobar->begin()); + EXPECT_EQ(6, foobar->end()); +} + +TEST(Printer, AnnotateEmptyRange) { + char buffer[8192]; + ArrayOutputStream output(buffer, sizeof(buffer)); + GeneratedCodeInfo info; + AnnotationProtoCollector info_collector(&info); + { + Printer printer(&output, '$', &info_collector); + printer.Print("012$foo$4$baz$$bam$$bar$\n", "foo", "3", "bar", "5", "baz", + "", "bam", ""); + vector path; + path.push_back(33); + MockDescriptor descriptor("path", path); + printer.Annotate("baz", "bam", &descriptor); + } + buffer[output.ByteCount()] = '\0'; + EXPECT_STREQ("012345\n", buffer); + ASSERT_EQ(1, info.annotation_size()); + const GeneratedCodeInfo::Annotation* bazbam = &info.annotation(0); + ASSERT_EQ(1, bazbam->path_size()); + EXPECT_EQ(33, bazbam->path(0)); + EXPECT_EQ("path", bazbam->source_file()); + EXPECT_EQ(5, bazbam->begin()); + EXPECT_EQ(5, bazbam->end()); +} + +TEST(Printer, AnnotateDespiteUnrelatedMultipleUses) { + char buffer[8192]; + ArrayOutputStream output(buffer, sizeof(buffer)); + GeneratedCodeInfo info; + AnnotationProtoCollector info_collector(&info); + { + Printer printer(&output, '$', &info_collector); + printer.Print("012$foo$4$foo$$bar$\n", "foo", "3", "bar", "5"); + vector path; + path.push_back(33); + MockDescriptor descriptor("path", path); + printer.Annotate("bar", "bar", &descriptor); + } + buffer[output.ByteCount()] = '\0'; + EXPECT_STREQ("0123435\n", buffer); + ASSERT_EQ(1, info.annotation_size()); + const GeneratedCodeInfo::Annotation* bar = &info.annotation(0); + ASSERT_EQ(1, bar->path_size()); + EXPECT_EQ(33, bar->path(0)); + EXPECT_EQ("path", bar->source_file()); + EXPECT_EQ(6, bar->begin()); + EXPECT_EQ(7, bar->end()); +} + TEST(Printer, Indenting) { char buffer[8192]; @@ -232,6 +423,52 @@ TEST(Printer, Death) { EXPECT_DEBUG_DEATH(printer.Print("$unclosed"), "Unclosed variable name"); EXPECT_DEBUG_DEATH(printer.Outdent(), "without matching Indent"); } + +TEST(Printer, AnnotateMultipleUsesDeath) { + char buffer[8192]; + ArrayOutputStream output(buffer, sizeof(buffer)); + GeneratedCodeInfo info; + AnnotationProtoCollector info_collector(&info); + { + Printer printer(&output, '$', &info_collector); + printer.Print("012$foo$4$foo$\n", "foo", "3"); + vector path; + path.push_back(33); + MockDescriptor descriptor("path", path); + EXPECT_DEBUG_DEATH(printer.Annotate("foo", "foo", &descriptor), "multiple"); + } +} + +TEST(Printer, AnnotateNegativeLengthDeath) { + char buffer[8192]; + ArrayOutputStream output(buffer, sizeof(buffer)); + GeneratedCodeInfo info; + AnnotationProtoCollector info_collector(&info); + { + Printer printer(&output, '$', &info_collector); + printer.Print("012$foo$4$bar$\n", "foo", "3", "bar", "5"); + vector path; + path.push_back(33); + MockDescriptor descriptor("path", path); + EXPECT_DEBUG_DEATH(printer.Annotate("bar", "foo", &descriptor), "negative"); + } +} + +TEST(Printer, AnnotateUndefinedDeath) { + char buffer[8192]; + ArrayOutputStream output(buffer, sizeof(buffer)); + GeneratedCodeInfo info; + AnnotationProtoCollector info_collector(&info); + { + Printer printer(&output, '$', &info_collector); + printer.Print("012$foo$4$foo$\n", "foo", "3"); + vector path; + path.push_back(33); + MockDescriptor descriptor("path", path); + EXPECT_DEBUG_DEATH(printer.Annotate("bar", "bar", &descriptor), + "Undefined"); + } +} #endif // PROTOBUF_HAS_DEATH_TEST TEST(Printer, WriteFailurePartial) { diff --git a/src/google/protobuf/io/tokenizer.cc b/src/google/protobuf/io/tokenizer.cc index 3d57707c1..b3550dfba 100644 --- a/src/google/protobuf/io/tokenizer.cc +++ b/src/google/protobuf/io/tokenizer.cc @@ -881,9 +881,11 @@ bool Tokenizer::ParseInteger(const string& text, uint64 max_value, uint64 result = 0; for (; *ptr != '\0'; ptr++) { int digit = DigitValue(*ptr); - GOOGLE_LOG_IF(DFATAL, digit < 0 || digit >= base) - << " Tokenizer::ParseInteger() passed text that could not have been" - " tokenized as an integer: " << CEscape(text); + if (digit < 0 || digit >= base) { + // The token provided by Tokenizer is invalid. i.e., 099 is an invalid + // token, but Tokenizer still think it's integer. + return false; + } if (digit > max_value || result > (max_value - digit) / base) { // Overflow. return false; diff --git a/src/google/protobuf/io/tokenizer.h b/src/google/protobuf/io/tokenizer.h index 49885eda9..64ee7d847 100644 --- a/src/google/protobuf/io/tokenizer.h +++ b/src/google/protobuf/io/tokenizer.h @@ -52,6 +52,12 @@ class ZeroCopyInputStream; // zero_copy_stream.h class ErrorCollector; class Tokenizer; +// By "column number", the proto compiler refers to a count of the number +// of bytes before a given byte, except that a tab character advances to +// the next multiple of 8 bytes. Note in particular that column numbers +// are zero-based, while many user interfaces use one-based column numbers. +typedef int ColumnNumber; + // Abstract interface for an object which collects the errors that occur // during parsing. A typical implementation might simply print the errors // to stdout. @@ -63,13 +69,14 @@ class LIBPROTOBUF_EXPORT ErrorCollector { // Indicates that there was an error in the input at the given line and // column numbers. The numbers are zero-based, so you may want to add // 1 to each before printing them. - virtual void AddError(int line, int column, const string& message) = 0; + virtual void AddError(int line, ColumnNumber column, + const string& message) = 0; // Indicates that there was a warning in the input at the given line and // column numbers. The numbers are zero-based, so you may want to add // 1 to each before printing them. - virtual void AddWarning(int /* line */, int /* column */, - const string& /* message */) { } + virtual void AddWarning(int line, ColumnNumber column, + const string& message) { } private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ErrorCollector); @@ -124,8 +131,8 @@ class LIBPROTOBUF_EXPORT Tokenizer { // "line" and "column" specify the position of the first character of // the token within the input stream. They are zero-based. int line; - int column; - int end_column; + ColumnNumber column; + ColumnNumber end_column; }; // Get the current token. This is updated when Next() is called. Before @@ -263,7 +270,7 @@ class LIBPROTOBUF_EXPORT Tokenizer { // Line and column number of current_char_ within the whole input stream. int line_; - int column_; + ColumnNumber column_; // String to which text should be appended as we advance through it. // Call RecordTo(&str) to start recording and StopRecording() to stop. @@ -280,6 +287,7 @@ class LIBPROTOBUF_EXPORT Tokenizer { // Since we count columns we need to interpret tabs somehow. We'll take // the standard 8-character definition for lack of any way to do better. + // This must match the documentation of ColumnNumber. static const int kTabWidth = 8; // ----------------------------------------------------------------- diff --git a/src/google/protobuf/io/tokenizer_unittest.cc b/src/google/protobuf/io/tokenizer_unittest.cc index 20d50a2c2..ae0811f89 100644 --- a/src/google/protobuf/io/tokenizer_unittest.cc +++ b/src/google/protobuf/io/tokenizer_unittest.cc @@ -736,19 +736,13 @@ TEST_F(TokenizerTest, ParseInteger) { EXPECT_EQ(0, ParseInteger("0x")); uint64 i; -#ifdef PROTOBUF_HAS_DEATH_TEST // death tests do not work on Windows yet + // Test invalid integers that will never be tokenized as integers. - EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("zxy", kuint64max, &i), - "passed text that could not have been tokenized as an integer"); - EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("1.2", kuint64max, &i), - "passed text that could not have been tokenized as an integer"); - EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("08", kuint64max, &i), - "passed text that could not have been tokenized as an integer"); - EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("0xg", kuint64max, &i), - "passed text that could not have been tokenized as an integer"); - EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("-1", kuint64max, &i), - "passed text that could not have been tokenized as an integer"); -#endif // PROTOBUF_HAS_DEATH_TEST + EXPECT_FALSE(Tokenizer::ParseInteger("zxy", kuint64max, &i)); + EXPECT_FALSE(Tokenizer::ParseInteger("1.2", kuint64max, &i)); + EXPECT_FALSE(Tokenizer::ParseInteger("08", kuint64max, &i)); + EXPECT_FALSE(Tokenizer::ParseInteger("0xg", kuint64max, &i)); + EXPECT_FALSE(Tokenizer::ParseInteger("-1", kuint64max, &i)); // Test overflows. EXPECT_TRUE (Tokenizer::ParseInteger("0", 0, &i)); diff --git a/src/google/protobuf/io/zero_copy_stream_impl_lite.cc b/src/google/protobuf/io/zero_copy_stream_impl_lite.cc index 083beca43..e6ca88c21 100644 --- a/src/google/protobuf/io/zero_copy_stream_impl_lite.cc +++ b/src/google/protobuf/io/zero_copy_stream_impl_lite.cc @@ -69,7 +69,7 @@ ArrayInputStream::~ArrayInputStream() { bool ArrayInputStream::Next(const void** data, int* size) { if (position_ < size_) { - last_returned_size_ = min(block_size_, size_ - position_); + last_returned_size_ = std::min(block_size_, size_ - position_); *data = data_ + position_; *size = last_returned_size_; position_ += last_returned_size_; @@ -122,7 +122,7 @@ ArrayOutputStream::~ArrayOutputStream() { bool ArrayOutputStream::Next(void** data, int* size) { if (position_ < size_) { - last_returned_size_ = min(block_size_, size_ - position_); + last_returned_size_ = std::min(block_size_, size_ - position_); *data = data_ + position_; *size = last_returned_size_; position_ += last_returned_size_; @@ -157,7 +157,7 @@ StringOutputStream::~StringOutputStream() { } bool StringOutputStream::Next(void** data, int* size) { - GOOGLE_CHECK_NE(NULL, target_); + GOOGLE_CHECK(target_ != NULL); int old_size = target_->size(); // Grow the string. @@ -177,9 +177,9 @@ bool StringOutputStream::Next(void** data, int* size) { // Double the size, also make sure that the new size is at least // kMinimumSize. STLStringResizeUninitialized( - target_, - max(old_size * 2, - kMinimumSize + 0)); // "+ 0" works around GCC4 weirdness. + target_, + std::max(old_size * 2, + kMinimumSize + 0)); // "+ 0" works around GCC4 weirdness. } *data = mutable_string_data(target_) + old_size; @@ -189,13 +189,13 @@ bool StringOutputStream::Next(void** data, int* size) { void StringOutputStream::BackUp(int count) { GOOGLE_CHECK_GE(count, 0); - GOOGLE_CHECK_NE(NULL, target_); + GOOGLE_CHECK(target_ != NULL); GOOGLE_CHECK_LE(count, target_->size()); target_->resize(target_->size() - count); } int64 StringOutputStream::ByteCount() const { - GOOGLE_CHECK_NE(NULL, target_); + GOOGLE_CHECK(target_ != NULL); return target_->size(); } @@ -235,8 +235,8 @@ int CopyingInputStream::Skip(int count) { char junk[4096]; int skipped = 0; while (skipped < count) { - int bytes = Read(junk, min(count - skipped, - implicit_cast(sizeof(junk)))); + int bytes = + Read(junk, std::min(count - skipped, implicit_cast(sizeof(junk)))); if (bytes <= 0) { // EOF or read error. return skipped; diff --git a/src/google/protobuf/io/zero_copy_stream_impl_lite.h b/src/google/protobuf/io/zero_copy_stream_impl_lite.h index 1c397deae..9d81ccfbf 100644 --- a/src/google/protobuf/io/zero_copy_stream_impl_lite.h +++ b/src/google/protobuf/io/zero_copy_stream_impl_lite.h @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -174,7 +175,7 @@ class LIBPROTOBUF_EXPORT LazyStringOutputStream : public StringOutputStream { int64 ByteCount() const; private: - const scoped_ptr > callback_; + const google::protobuf::scoped_ptr > callback_; bool string_is_set_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(LazyStringOutputStream); diff --git a/src/google/protobuf/io/zero_copy_stream_unittest.cc b/src/google/protobuf/io/zero_copy_stream_unittest.cc index 8c7358c19..a9db8872d 100644 --- a/src/google/protobuf/io/zero_copy_stream_unittest.cc +++ b/src/google/protobuf/io/zero_copy_stream_unittest.cc @@ -63,8 +63,9 @@ #endif #include -#include +#include #include +#include #if HAVE_ZLIB #include @@ -72,7 +73,6 @@ #include #include -#include #include #include #include diff --git a/src/google/protobuf/lite_unittest.cc b/src/google/protobuf/lite_unittest.cc index d1948ab5c..3ca3fbaf6 100644 --- a/src/google/protobuf/lite_unittest.cc +++ b/src/google/protobuf/lite_unittest.cc @@ -686,6 +686,33 @@ int main(int argc, char* argv[]) { EXPECT_TRUE(map_message.IsInitialized()); } + { + // Check that adding more values to enum does not corrupt message + // when passed through an old client. + protobuf_unittest::V2MessageLite v2_message; + v2_message.set_int_field(800); + // Set enum field to the value not understood by the old client. + v2_message.set_enum_field(protobuf_unittest::V2_SECOND); + string v2_bytes = v2_message.SerializeAsString(); + + protobuf_unittest::V1MessageLite v1_message; + v1_message.ParseFromString(v2_bytes); + EXPECT_TRUE(v1_message.IsInitialized()); + EXPECT_EQ(v1_message.int_field(), v2_message.int_field()); + // V1 client does not understand V2_SECOND value, so it discards it and + // uses default value instead. + EXPECT_EQ(v1_message.enum_field(), protobuf_unittest::V1_FIRST); + + // However, when re-serialized, it should preserve enum value. + string v1_bytes = v1_message.SerializeAsString(); + + protobuf_unittest::V2MessageLite same_v2_message; + same_v2_message.ParseFromString(v1_bytes); + + EXPECT_EQ(v2_message.int_field(), same_v2_message.int_field()); + EXPECT_EQ(v2_message.enum_field(), same_v2_message.enum_field()); + } + std::cout << "PASS" << std::endl; return 0; } diff --git a/src/google/protobuf/map.h b/src/google/protobuf/map.h index 37e19b0a7..bb0b14f9c 100644 --- a/src/google/protobuf/map.h +++ b/src/google/protobuf/map.h @@ -31,9 +31,11 @@ #ifndef GOOGLE_PROTOBUF_MAP_H__ #define GOOGLE_PROTOBUF_MAP_H__ -#include #include +#include #include // To support Visual Studio 2008 +#include +#include #include #include @@ -41,17 +43,23 @@ #include #include #include +#if __cpp_exceptions && LANG_CXX11 +#include +#endif namespace google { namespace protobuf { +// The Map and MapIterator types are provided by this header file. +// Please avoid using other types defined here, unless they are public +// types within Map or MapIterator, such as Map::value_type. template class Map; -template struct is_proto_enum; - class MapIterator; +template struct is_proto_enum; + namespace internal { template m0, m1; +// m0[0] = m1[0] = m0[1] = m1[1] = 0; +// assert(m0.begin()->first == m1.begin()->first); // Bug! +// +// Map's interface is similar to std::unordered_map, except that Map is not +// designed to play well with exceptions. Mutations to a Map do not invalidate +// a Map's iterators, pointers to elements, or references to elements. Except +// for erase(iterator), any non-const method can reorder iterators. template class Map { public: @@ -473,40 +521,56 @@ class Map { typedef size_t size_type; typedef hash hasher; - typedef equal_to key_equal; - Map() + Map(bool old_style = true) : arena_(NULL), - allocator_(arena_), - elements_(0, hasher(), key_equal(), allocator_), - default_enum_value_(0) {} - explicit Map(Arena* arena) - : arena_(arena), - allocator_(arena_), - elements_(0, hasher(), key_equal(), allocator_), - default_enum_value_(0) { - arena_->OwnDestructor(&elements_); + default_enum_value_(0), + old_style_(old_style) { + Init(); + } + explicit Map(Arena* arena, bool old_style = true) + : arena_(arena), + default_enum_value_(0), + old_style_(old_style) { + Init(); } - Map(const Map& other) : arena_(NULL), - allocator_(arena_), - elements_(0, hasher(), key_equal(), allocator_), - default_enum_value_(other.default_enum_value_) { + default_enum_value_(other.default_enum_value_), + old_style_(other.old_style_) { + Init(); insert(other.begin(), other.end()); } template - Map(const InputIt& first, const InputIt& last) + Map(const InputIt& first, const InputIt& last, bool old_style = true) : arena_(NULL), - allocator_(arena_), - elements_(0, hasher(), key_equal(), allocator_), - default_enum_value_(0) { + default_enum_value_(0), + old_style_(old_style) { + Init(); insert(first, last); } - ~Map() { clear(); } + ~Map() { + clear(); + if (arena_ == NULL) { + if (old_style_) + delete deprecated_elements_; + else + delete elements_; + } + } private: + void Init() { + if (old_style_) + deprecated_elements_ = Arena::Create( + arena_, 0, hasher(), equal_to(), + MapAllocator*> >(arena_)); + else + elements_ = + Arena::Create(arena_, 0, hasher(), Allocator(arena_)); + } + // re-implement std::allocator to use arena allocator for memory allocation. // Used for google::protobuf::Map implementation. Users should not use this class // directly. @@ -545,14 +609,17 @@ class Map { } #if __cplusplus >= 201103L && !defined(GOOGLE_PROTOBUF_OS_APPLE) && \ - !defined(GOOGLE_PROTOBUF_OS_NACL) && !defined(GOOGLE_PROTOBUF_OS_ANDROID) + !defined(GOOGLE_PROTOBUF_OS_NACL) && \ + !defined(GOOGLE_PROTOBUF_OS_ANDROID) && \ + !defined(GOOGLE_PROTOBUF_OS_EMSCRIPTEN) template void construct(NodeType* p, Args&&... args) { - // Clang 3.6 doesn't compile static casting to void* directly. (Issue #1266) - // According C++ standard 5.2.9/1: "The static_cast operator shall not cast - // away constness". So first the maybe const pointer is casted to const void* and - // after the const void* is const casted. - new (const_cast(static_cast(p))) NodeType(std::forward(args)...); + // Clang 3.6 doesn't compile static casting to void* directly. (Issue + // #1266) According C++ standard 5.2.9/1: "The static_cast operator shall + // not cast away constness". So first the maybe const pointer is casted to + // const void* and after the const void* is const casted. + new (const_cast(static_cast(p))) + NodeType(std::forward(args)...); } template @@ -593,86 +660,849 @@ class Map { friend class MapAllocator; }; - typedef MapAllocator*> > Allocator; - typedef hash_map, equal_to, Allocator> - InnerMap; + // InnerMap's key type is Key and its value type is value_type*. We use a + // custom class here and for Node, below, to ensure that k_ is at offset 0, + // allowing safe conversion from pointer to Node to pointer to Key, and vice + // versa when appropriate. + class KeyValuePair { + public: + KeyValuePair(const Key& k, value_type* v) : k_(k), v_(v) {} + + const Key& key() const { return k_; } + Key& key() { return k_; } + value_type* const value() const { return v_; } + value_type*& value() { return v_; } + + private: + Key k_; + value_type* v_; + }; + + typedef MapAllocator Allocator; + + // InnerMap is a generic hash-based map. It doesn't contain any + // protocol-buffer-specific logic. It is a chaining hash map with the + // additional feature that some buckets can be converted to use an ordered + // container. This ensures O(lg n) bounds on find, insert, and erase, while + // avoiding the overheads of ordered containers most of the time. + // + // The implementation doesn't need the full generality of unordered_map, + // and it doesn't have it. More bells and whistles can be added as needed. + // Some implementation details: + // 1. The hash function has type hasher and the equality function + // equal_to. We inherit from hasher to save space + // (empty-base-class optimization). + // 2. The number of buckets is a power of two. + // 3. Buckets are converted to trees in pairs: if we convert bucket b then + // buckets b and b^1 will share a tree. Invariant: buckets b and b^1 have + // the same non-NULL value iff they are sharing a tree. (An alternative + // implementation strategy would be to have a tag bit per bucket.) + // 4. As is typical for hash_map and such, the Keys and Values are always + // stored in linked list nodes. Pointers to elements are never invalidated + // until the element is deleted. + // 5. The trees' payload type is pointer to linked-list node. Tree-converting + // a bucket doesn't copy Key-Value pairs. + // 6. Once we've tree-converted a bucket, it is never converted back. However, + // the items a tree contains may wind up assigned to trees or lists upon a + // rehash. + // 7. The code requires no C++ features from C++11 or later. + // 8. Mutations to a map do not invalidate the map's iterators, pointers to + // elements, or references to elements. + // 9. Except for erase(iterator), any non-const method can reorder iterators. + class InnerMap : private hasher { + public: + typedef value_type* Value; + + InnerMap(size_type n, hasher h, Allocator alloc) + : hasher(h), + num_elements_(0), + seed_(Seed()), + table_(NULL), + alloc_(alloc) { + n = TableSize(n); + table_ = CreateEmptyTable(n); + num_buckets_ = index_of_first_non_null_ = n; + } + + ~InnerMap() { + if (table_ != NULL) { + clear(); + Dealloc(table_, num_buckets_); + } + } + + private: + enum { kMinTableSize = 8 }; + + // Linked-list nodes, as one would expect for a chaining hash table. + struct Node { + KeyValuePair kv; + Node* next; + }; + + // This is safe only if the given pointer is known to point to a Key that is + // part of a Node. + static Node* NodePtrFromKeyPtr(Key* k) { + return reinterpret_cast(k); + } + + static Key* KeyPtrFromNodePtr(Node* node) { return &node->kv.key(); } + + // Trees. The payload type is pointer to Key, so that we can query the tree + // with Keys that are not in any particular data structure. When we insert, + // though, the pointer is always pointing to a Key that is inside a Node. + struct KeyCompare { + bool operator()(const Key* n0, const Key* n1) const { return *n0 < *n1; } + }; + typedef typename Allocator::template rebind::other KeyPtrAllocator; + typedef std::set Tree; + + // iterator and const_iterator are instantiations of iterator_base. + template + class iterator_base { + public: + typedef KeyValueType& reference; + typedef KeyValueType* pointer; + typedef typename Tree::iterator TreeIterator; + + // Invariants: + // node_ is always correct. This is handy because the most common + // operations are operator* and operator-> and they only use node_. + // When node_ is set to a non-NULL value, all the other non-const fields + // are updated to be correct also, but those fields can become stale + // if the underlying map is modified. When those fields are needed they + // are rechecked, and updated if necessary. + iterator_base() : node_(NULL) {} + + explicit iterator_base(const InnerMap* m) : m_(m) { + SearchFrom(m->index_of_first_non_null_); + } + + // Any iterator_base can convert to any other. This is overkill, and we + // rely on the enclosing class to use it wisely. The standard "iterator + // can convert to const_iterator" is OK but the reverse direction is not. + template + explicit iterator_base(const iterator_base& it) + : node_(it.node_), + m_(it.m_), + bucket_index_(it.bucket_index_), + tree_it_(it.tree_it_) {} + + iterator_base(Node* n, const InnerMap* m, size_type index) + : node_(n), + m_(m), + bucket_index_(index) {} + + iterator_base(TreeIterator tree_it, const InnerMap* m, size_type index) + : node_(NodePtrFromKeyPtr(*tree_it)), + m_(m), + bucket_index_(index), + tree_it_(tree_it) { + // Invariant: iterators that use tree_it_ have an even bucket_index_. + GOOGLE_DCHECK_EQ(bucket_index_ % 2, 0); + } + + // Advance through buckets, looking for the first that isn't empty. + // If nothing non-empty is found then leave node_ == NULL. + void SearchFrom(size_type start_bucket) { + GOOGLE_DCHECK(m_->index_of_first_non_null_ == m_->num_buckets_ || + m_->table_[m_->index_of_first_non_null_] != NULL); + node_ = NULL; + for (bucket_index_ = start_bucket; bucket_index_ < m_->num_buckets_; + bucket_index_++) { + if (m_->TableEntryIsNonEmptyList(bucket_index_)) { + node_ = static_cast(m_->table_[bucket_index_]); + break; + } else if (m_->TableEntryIsTree(bucket_index_)) { + Tree* tree = static_cast(m_->table_[bucket_index_]); + GOOGLE_DCHECK(!tree->empty()); + tree_it_ = tree->begin(); + node_ = NodePtrFromKeyPtr(*tree_it_); + break; + } + } + } + + reference operator*() const { return node_->kv; } + pointer operator->() const { return &(operator*()); } + + friend bool operator==(const iterator_base& a, const iterator_base& b) { + return a.node_ == b.node_; + } + friend bool operator!=(const iterator_base& a, const iterator_base& b) { + return a.node_ != b.node_; + } + + iterator_base& operator++() { + if (node_->next == NULL) { + const bool is_list = revalidate_if_necessary(); + if (is_list) { + SearchFrom(bucket_index_ + 1); + } else { + GOOGLE_DCHECK_EQ(bucket_index_ & 1, 0); + Tree* tree = static_cast(m_->table_[bucket_index_]); + if (++tree_it_ == tree->end()) { + SearchFrom(bucket_index_ + 2); + } else { + node_ = NodePtrFromKeyPtr(*tree_it_); + } + } + } else { + node_ = node_->next; + } + return *this; + } + + iterator_base operator++(int /* unused */) { + iterator_base tmp = *this; + ++*this; + return tmp; + } + + // Assumes node_ and m_ are correct and non-NULL, but other fields may be + // stale. Fix them as needed. Then return true iff node_ points to a + // Node in a list. + bool revalidate_if_necessary() { + GOOGLE_DCHECK(node_ != NULL && m_ != NULL); + // Force bucket_index_ to be in range. + bucket_index_ &= (m_->num_buckets_ - 1); + // Common case: the bucket we think is relevant points to node_. + if (m_->table_[bucket_index_] == static_cast(node_)) + return true; + // Less common: the bucket is a linked list with node_ somewhere in it, + // but not at the head. + if (m_->TableEntryIsNonEmptyList(bucket_index_)) { + Node* l = static_cast(m_->table_[bucket_index_]); + while ((l = l->next) != NULL) { + if (l == node_) { + return true; + } + } + } + // Well, bucket_index_ still might be correct, but probably + // not. Revalidate just to be sure. This case is rare enough that we + // don't worry about potential optimizations, such as having a custom + // find-like method that compares Node* instead of const Key&. + iterator_base i(m_->find(*KeyPtrFromNodePtr(node_))); + bucket_index_ = i.bucket_index_; + tree_it_ = i.tree_it_; + return m_->TableEntryIsList(bucket_index_); + } + + Node* node_; + const InnerMap* m_; + size_type bucket_index_; + TreeIterator tree_it_; + }; + + public: + typedef iterator_base iterator; + typedef iterator_base const_iterator; + + iterator begin() { return iterator(this); } + iterator end() { return iterator(); } + const_iterator begin() const { return const_iterator(this); } + const_iterator end() const { return const_iterator(); } + + void clear() { + for (size_type b = 0; b < num_buckets_; b++) { + if (TableEntryIsNonEmptyList(b)) { + Node* node = static_cast(table_[b]); + table_[b] = NULL; + do { + Node* next = node->next; + DestroyNode(node); + node = next; + } while (node != NULL); + } else if (TableEntryIsTree(b)) { + Tree* tree = static_cast(table_[b]); + GOOGLE_DCHECK(table_[b] == table_[b + 1] && (b & 1) == 0); + table_[b] = table_[b + 1] = NULL; + typename Tree::iterator tree_it = tree->begin(); + do { + Node* node = NodePtrFromKeyPtr(*tree_it); + typename Tree::iterator next = tree_it; + ++next; + tree->erase(tree_it); + DestroyNode(node); + tree_it = next; + } while (tree_it != tree->end()); + DestroyTree(tree); + b++; + } + } + num_elements_ = 0; + index_of_first_non_null_ = num_buckets_; + } + + const hasher& hash_function() const { return *this; } + + static size_type max_size() { + return static_cast(1) << (sizeof(void**) >= 8 ? 60 : 28); + } + size_type size() const { return num_elements_; } + bool empty() const { return size() == 0; } + + iterator find(const Key& k) { return iterator(FindHelper(k).first); } + const_iterator find(const Key& k) const { return FindHelper(k).first; } + + // In traditional C++ style, this performs "insert if not present." + std::pair insert(const KeyValuePair& kv) { + std::pair p = FindHelper(kv.key()); + // Case 1: key was already present. + if (p.first.node_ != NULL) + return std::make_pair(iterator(p.first), false); + // Case 2: insert. + if (ResizeIfLoadIsOutOfRange(num_elements_ + 1)) { + p = FindHelper(kv.key()); + } + const size_type b = p.second; // bucket number + Node* node = Alloc(1); + alloc_.construct(&node->kv, kv); + iterator result = InsertUnique(b, node); + ++num_elements_; + return std::make_pair(result, true); + } + + // The same, but if an insertion is necessary then the value portion of the + // inserted key-value pair is left uninitialized. + std::pair insert(const Key& k) { + std::pair p = FindHelper(k); + // Case 1: key was already present. + if (p.first.node_ != NULL) + return std::make_pair(iterator(p.first), false); + // Case 2: insert. + if (ResizeIfLoadIsOutOfRange(num_elements_ + 1)) { + p = FindHelper(k); + } + const size_type b = p.second; // bucket number + Node* node = Alloc(1); + typedef typename Allocator::template rebind::other KeyAllocator; + KeyAllocator(alloc_).construct(&node->kv.key(), k); + iterator result = InsertUnique(b, node); + ++num_elements_; + return std::make_pair(result, true); + } + + Value& operator[](const Key& k) { + KeyValuePair kv(k, Value()); + return insert(kv).first->value(); + } + + void erase(iterator it) { + GOOGLE_DCHECK_EQ(it.m_, this); + const bool is_list = it.revalidate_if_necessary(); + size_type b = it.bucket_index_; + Node* const item = it.node_; + if (is_list) { + GOOGLE_DCHECK(TableEntryIsNonEmptyList(b)); + Node* head = static_cast(table_[b]); + head = EraseFromLinkedList(item, head); + table_[b] = static_cast(head); + } else { + GOOGLE_DCHECK(TableEntryIsTree(b)); + Tree* tree = static_cast(table_[b]); + tree->erase(it.tree_it_); + if (tree->empty()) { + // Force b to be the minimum of b and b ^ 1. This is important + // only because we want index_of_first_non_null_ to be correct. + b &= ~static_cast(1); + DestroyTree(tree); + table_[b] = table_[b + 1] = NULL; + } + } + DestroyNode(item); + --num_elements_; + if (GOOGLE_PREDICT_FALSE(b == index_of_first_non_null_)) { + while (index_of_first_non_null_ < num_buckets_ && + table_[index_of_first_non_null_] == NULL) { + ++index_of_first_non_null_; + } + } + } + + private: + std::pair FindHelper(const Key& k) const { + size_type b = BucketNumber(k); + if (TableEntryIsNonEmptyList(b)) { + Node* node = static_cast(table_[b]); + do { + if (IsMatch(*KeyPtrFromNodePtr(node), k)) { + return std::make_pair(const_iterator(node, this, b), b); + } else { + node = node->next; + } + } while (node != NULL); + } else if (TableEntryIsTree(b)) { + GOOGLE_DCHECK_EQ(table_[b], table_[b ^ 1]); + b &= ~static_cast(1); + Tree* tree = static_cast(table_[b]); + Key* key = const_cast(&k); + typename Tree::iterator tree_it = tree->find(key); + if (tree_it != tree->end()) { + return std::make_pair(const_iterator(tree_it, this, b), b); + } + } + return std::make_pair(end(), b); + } + + // Insert the given Node in bucket b. If that would make bucket b too big, + // and bucket b is not a tree, create a tree for buckets b and b^1 to share. + // Requires count(*KeyPtrFromNodePtr(node)) == 0 and that b is the correct + // bucket. num_elements_ is not modified. + iterator InsertUnique(size_type b, Node* node) { + GOOGLE_DCHECK(index_of_first_non_null_ == num_buckets_ || + table_[index_of_first_non_null_] != NULL); + // In practice, the code that led to this point may have already + // determined whether we are inserting into an empty list, a short list, + // or whatever. But it's probably cheap enough to recompute that here; + // it's likely that we're inserting into an empty or short list. + iterator result; + GOOGLE_DCHECK(find(*KeyPtrFromNodePtr(node)) == end()); + if (TableEntryIsEmpty(b)) { + result = InsertUniqueInList(b, node); + } else if (TableEntryIsNonEmptyList(b)) { + if (GOOGLE_PREDICT_FALSE(TableEntryIsTooLong(b))) { + TreeConvert(b); + result = InsertUniqueInTree(b, node); + GOOGLE_DCHECK_EQ(result.bucket_index_, b & ~static_cast(1)); + } else { + // Insert into a pre-existing list. This case cannot modify + // index_of_first_non_null_, so we skip the code to update it. + return InsertUniqueInList(b, node); + } + } else { + // Insert into a pre-existing tree. This case cannot modify + // index_of_first_non_null_, so we skip the code to update it. + return InsertUniqueInTree(b, node); + } + index_of_first_non_null_ = + std::min(index_of_first_non_null_, result.bucket_index_); + return result; + } + + // Helper for InsertUnique. Handles the case where bucket b is a + // not-too-long linked list. + iterator InsertUniqueInList(size_type b, Node* node) { + node->next = static_cast(table_[b]); + table_[b] = static_cast(node); + return iterator(node, this, b); + } + + // Helper for InsertUnique. Handles the case where bucket b points to a + // Tree. + iterator InsertUniqueInTree(size_type b, Node* node) { + GOOGLE_DCHECK_EQ(table_[b], table_[b ^ 1]); + // Maintain the invariant that node->next is NULL for all Nodes in Trees. + node->next = NULL; + return iterator(static_cast(table_[b]) + ->insert(KeyPtrFromNodePtr(node)) + .first, + this, b & ~static_cast(1)); + } + + // Returns whether it did resize. Currently this is only used when + // num_elements_ increases, though it could be used in other situations. + // It checks for load too low as well as load too high: because any number + // of erases can occur between inserts, the load could be as low as 0 here. + // Resizing to a lower size is not always helpful, but failing to do so can + // destroy the expected big-O bounds for some operations. By having the + // policy that sometimes we resize down as well as up, clients can easily + // keep O(size()) = O(number of buckets) if they want that. + bool ResizeIfLoadIsOutOfRange(size_type new_size) { + const size_type kMaxMapLoadTimes16 = 12; // controls RAM vs CPU tradeoff + const size_type hi_cutoff = num_buckets_ * kMaxMapLoadTimes16 / 16; + const size_type lo_cutoff = hi_cutoff / 4; + // We don't care how many elements are in trees. If a lot are, + // we may resize even though there are many empty buckets. In + // practice, this seems fine. + if (GOOGLE_PREDICT_FALSE(new_size >= hi_cutoff)) { + if (num_buckets_ <= max_size() / 2) { + Resize(num_buckets_ * 2); + return true; + } + } else if (GOOGLE_PREDICT_FALSE(new_size <= lo_cutoff && + num_buckets_ > kMinTableSize)) { + size_type lg2_of_size_reduction_factor = 1; + // It's possible we want to shrink a lot here... size() could even be 0. + // So, estimate how much to shrink by making sure we don't shrink so + // much that we would need to grow the table after a few inserts. + const size_type hypothetical_size = new_size * 5 / 4 + 1; + while ((hypothetical_size << lg2_of_size_reduction_factor) < + hi_cutoff) { + ++lg2_of_size_reduction_factor; + } + size_type new_num_buckets = std::max( + kMinTableSize, num_buckets_ >> lg2_of_size_reduction_factor); + if (new_num_buckets != num_buckets_) { + Resize(new_num_buckets); + return true; + } + } + return false; + } + + // Resize to the given number of buckets. + void Resize(size_t new_num_buckets) { + GOOGLE_DCHECK_GE(new_num_buckets, kMinTableSize); + void** const old_table = table_; + const size_type old_table_size = num_buckets_; + num_buckets_ = new_num_buckets; + table_ = CreateEmptyTable(num_buckets_); + const size_type start = index_of_first_non_null_; + index_of_first_non_null_ = num_buckets_; + for (size_type i = start; i < old_table_size; i++) { + if (TableEntryIsNonEmptyList(old_table, i)) { + TransferList(old_table, i); + } else if (TableEntryIsTree(old_table, i)) { + TransferTree(old_table, i++); + } + } + Dealloc(old_table, old_table_size); + } + + void TransferList(void* const* table, size_type index) { + Node* node = static_cast(table[index]); + do { + Node* next = node->next; + InsertUnique(BucketNumber(*KeyPtrFromNodePtr(node)), node); + node = next; + } while (node != NULL); + } + + void TransferTree(void* const* table, size_type index) { + Tree* tree = static_cast(table[index]); + typename Tree::iterator tree_it = tree->begin(); + do { + Node* node = NodePtrFromKeyPtr(*tree_it); + InsertUnique(BucketNumber(**tree_it), node); + } while (++tree_it != tree->end()); + DestroyTree(tree); + } + + Node* EraseFromLinkedList(Node* item, Node* head) { + if (head == item) { + return head->next; + } else { + head->next = EraseFromLinkedList(item, head->next); + return head; + } + } + + bool TableEntryIsEmpty(size_type b) const { + return TableEntryIsEmpty(table_, b); + } + bool TableEntryIsNonEmptyList(size_type b) const { + return TableEntryIsNonEmptyList(table_, b); + } + bool TableEntryIsTree(size_type b) const { + return TableEntryIsTree(table_, b); + } + bool TableEntryIsList(size_type b) const { + return TableEntryIsList(table_, b); + } + static bool TableEntryIsEmpty(void* const* table, size_type b) { + return table[b] == NULL; + } + static bool TableEntryIsNonEmptyList(void* const* table, size_type b) { + return table[b] != NULL && table[b] != table[b ^ 1]; + } + static bool TableEntryIsTree(void* const* table, size_type b) { + return !TableEntryIsEmpty(table, b) && + !TableEntryIsNonEmptyList(table, b); + } + static bool TableEntryIsList(void* const* table, size_type b) { + return !TableEntryIsTree(table, b); + } + + void TreeConvert(size_type b) { + GOOGLE_DCHECK(!TableEntryIsTree(b) && !TableEntryIsTree(b ^ 1)); + typename Allocator::template rebind::other tree_allocator(alloc_); + Tree* tree = tree_allocator.allocate(1); + // We want to use the three-arg form of construct, if it exists, but we + // create a temporary and use the two-arg construct that's known to exist. + // It's clunky, but the compiler should be able to generate more-or-less + // the same code. + tree_allocator.construct(tree, + Tree(KeyCompare(), KeyPtrAllocator(alloc_))); + // Now the tree is ready to use. + size_type count = CopyListToTree(b, tree) + CopyListToTree(b ^ 1, tree); + GOOGLE_DCHECK_EQ(count, tree->size()); + table_[b] = table_[b ^ 1] = static_cast(tree); + } + + // Copy a linked list in the given bucket to a tree. + // Returns the number of things it copied. + size_type CopyListToTree(size_type b, Tree* tree) { + size_type count = 0; + Node* node = static_cast(table_[b]); + while (node != NULL) { + tree->insert(KeyPtrFromNodePtr(node)); + ++count; + Node* next = node->next; + node->next = NULL; + node = next; + } + return count; + } + + // Return whether table_[b] is a linked list that seems awfully long. + // Requires table_[b] to point to a non-empty linked list. + bool TableEntryIsTooLong(size_type b) { + const int kMaxLength = 8; + size_type count = 0; + Node* node = static_cast(table_[b]); + do { + ++count; + node = node->next; + } while (node != NULL); + // Invariant: no linked list ever is more than kMaxLength in length. + GOOGLE_DCHECK_LE(count, kMaxLength); + return count >= kMaxLength; + } + + size_type BucketNumber(const Key& k) const { + // We inherit from hasher, so one-arg operator() provides a hash function. + size_type h = (*const_cast(this))(k); + // To help prevent people from making assumptions about the hash function, + // we use the seed differently depending on NDEBUG. The default hash + // function, the seeding, etc., are all likely to change in the future. +#ifndef NDEBUG + return (h * (seed_ | 1)) & (num_buckets_ - 1); +#else + return (h + seed_) & (num_buckets_ - 1); +#endif + } + + bool IsMatch(const Key& k0, const Key& k1) const { + return std::equal_to()(k0, k1); + } + + // Return a power of two no less than max(kMinTableSize, n). + // Assumes either n < kMinTableSize or n is a power of two. + size_type TableSize(size_type n) { + return n < kMinTableSize ? kMinTableSize : n; + } + + // Use alloc_ to allocate an array of n objects of type U. + template + U* Alloc(size_type n) { + typedef typename Allocator::template rebind::other alloc_type; + return alloc_type(alloc_).allocate(n); + } + + // Use alloc_ to deallocate an array of n objects of type U. + template + void Dealloc(U* t, size_type n) { + typedef typename Allocator::template rebind::other alloc_type; + alloc_type(alloc_).deallocate(t, n); + } + + void DestroyNode(Node* node) { + alloc_.destroy(&node->kv); + Dealloc(node, 1); + } + + void DestroyTree(Tree* tree) { + typename Allocator::template rebind::other tree_allocator(alloc_); + tree_allocator.destroy(tree); + tree_allocator.deallocate(tree, 1); + } + + void** CreateEmptyTable(size_type n) { + GOOGLE_DCHECK(n >= kMinTableSize); + GOOGLE_DCHECK_EQ(n & (n - 1), 0); + void** result = Alloc(n); + memset(result, 0, n * sizeof(result[0])); + return result; + } + + // Return a randomish value. + size_type Seed() const { + // random_device can throw, so avoid it unless we are compiling with + // exceptions enabled. +#if __cpp_exceptions && LANG_CXX11 + try { + std::random_device rd; + std::knuth_b knuth(rd()); + std::uniform_int_distribution u; + return u(knuth); + } catch (...) { } +#endif + size_type s = static_cast(reinterpret_cast(this)); +#if defined(__x86_64__) && defined(__GNUC__) + uint32 hi, lo; + asm("rdtsc" : "=a" (lo), "=d" (hi)); + s += ((static_cast(hi) << 32) | lo); +#endif + return s; + } + + size_type num_elements_; + size_type num_buckets_; + size_type seed_; + size_type index_of_first_non_null_; + void** table_; // an array with num_buckets_ entries + Allocator alloc_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(InnerMap); + }; // end of class InnerMap + + typedef hash_map, equal_to, + MapAllocator*> > > + DeprecatedInnerMap; public: // Iterators + class iterator_base { + public: + // We support "old style" and "new style" iterators for now. This is + // temporary. Also, for "iterator()" we have an unknown category. + // TODO(gpike): get rid of this. + enum IteratorStyle { kUnknown, kOld, kNew }; + explicit iterator_base(IteratorStyle style) : iterator_style_(style) {} + + bool OldStyle() const { + GOOGLE_DCHECK_NE(iterator_style_, kUnknown); + return iterator_style_ == kOld; + } + bool UnknownStyle() const { + return iterator_style_ == kUnknown; + } + bool SameStyle(const iterator_base& other) const { + return iterator_style_ == other.iterator_style_; + } + + private: + IteratorStyle iterator_style_; + }; + class const_iterator - : public std::iterator { typedef typename InnerMap::const_iterator InnerIt; + typedef typename DeprecatedInnerMap::const_iterator DeprecatedInnerIt; public: - const_iterator() {} - explicit const_iterator(const InnerIt& it) : it_(it) {} + const_iterator() : iterator_base(iterator_base::kUnknown) {} + explicit const_iterator(const DeprecatedInnerIt& dit) + : iterator_base(iterator_base::kOld), dit_(dit) {} + explicit const_iterator(const InnerIt& it) + : iterator_base(iterator_base::kNew), it_(it) {} - const_reference operator*() const { return *it_->second; } - const_pointer operator->() const { return it_->second; } + const_iterator(const const_iterator& other) + : iterator_base(other), it_(other.it_), dit_(other.dit_) {} + + const_reference operator*() const { + return this->OldStyle() ? *dit_->second : *it_->value(); + } + const_pointer operator->() const { return &(operator*()); } const_iterator& operator++() { - ++it_; + if (this->OldStyle()) + ++dit_; + else + ++it_; return *this; } - const_iterator operator++(int) { return const_iterator(it_++); } + const_iterator operator++(int) { + return this->OldStyle() ? const_iterator(dit_++) : const_iterator(it_++); + } friend bool operator==(const const_iterator& a, const const_iterator& b) { - return a.it_ == b.it_; + if (!a.SameStyle(b)) return false; + if (a.UnknownStyle()) return true; + return a.OldStyle() ? (a.dit_ == b.dit_) : (a.it_ == b.it_); } friend bool operator!=(const const_iterator& a, const const_iterator& b) { - return a.it_ != b.it_; + return !(a == b); } private: InnerIt it_; + DeprecatedInnerIt dit_; }; - class iterator : public std::iterator { + class iterator : private iterator_base, + public std::iterator { typedef typename InnerMap::iterator InnerIt; + typedef typename DeprecatedInnerMap::iterator DeprecatedInnerIt; public: - iterator() {} - explicit iterator(const InnerIt& it) : it_(it) {} + iterator() : iterator_base(iterator_base::kUnknown) {} + explicit iterator(const DeprecatedInnerIt& dit) + : iterator_base(iterator_base::kOld), dit_(dit) {} + explicit iterator(const InnerIt& it) + : iterator_base(iterator_base::kNew), it_(it) {} - reference operator*() const { return *it_->second; } - pointer operator->() const { return it_->second; } + reference operator*() const { + return this->OldStyle() ? *dit_->second : *it_->value(); + } + pointer operator->() const { return &(operator*()); } iterator& operator++() { - ++it_; + if (this->OldStyle()) + ++dit_; + else + ++it_; return *this; } - iterator operator++(int) { return iterator(it_++); } + iterator operator++(int) { + return this->OldStyle() ? iterator(dit_++) : iterator(it_++); + } - // Implicitly convertible to const_iterator. - operator const_iterator() const { return const_iterator(it_); } + // Allow implicit conversion to const_iterator. + operator const_iterator() const { + return this->OldStyle() ? + const_iterator(typename DeprecatedInnerMap::const_iterator(dit_)) : + const_iterator(typename InnerMap::const_iterator(it_)); + } friend bool operator==(const iterator& a, const iterator& b) { - return a.it_ == b.it_; + if (!a.SameStyle(b)) return false; + if (a.UnknownStyle()) return true; + return a.OldStyle() ? a.dit_ == b.dit_ : a.it_ == b.it_; } friend bool operator!=(const iterator& a, const iterator& b) { - return a.it_ != b.it_; + return !(a == b); } private: friend class Map; + InnerIt it_; + DeprecatedInnerIt dit_; }; - iterator begin() { return iterator(elements_.begin()); } - iterator end() { return iterator(elements_.end()); } - const_iterator begin() const { return const_iterator(elements_.begin()); } - const_iterator end() const { return const_iterator(elements_.end()); } + iterator begin() { + return old_style_ ? iterator(deprecated_elements_->begin()) + : iterator(elements_->begin()); + } + iterator end() { + return old_style_ ? iterator(deprecated_elements_->end()) + : iterator(elements_->end()); + } + const_iterator begin() const { + return old_style_ ? const_iterator(deprecated_elements_->begin()) + : const_iterator(iterator(elements_->begin())); + } + const_iterator end() const { + return old_style_ ? const_iterator(deprecated_elements_->end()) + : const_iterator(iterator(elements_->end())); + } const_iterator cbegin() const { return begin(); } const_iterator cend() const { return end(); } // Capacity - size_type size() const { return elements_.size(); } - bool empty() const { return elements_.empty(); } + size_type size() const { + return old_style_ ? deprecated_elements_->size() : elements_->size(); + } + bool empty() const { return size() == 0; } // Element access T& operator[](const key_type& key) { - value_type** value = &elements_[key]; + value_type** value = + old_style_ ? &(*deprecated_elements_)[key] : &(*elements_)[key]; if (*value == NULL) { *value = CreateValueTypeInternal(key); internal::MapValueInitializer::value, @@ -694,13 +1524,16 @@ class Map { // Lookup size_type count(const key_type& key) const { - return elements_.count(key); + if (find(key) != end()) assert(key == find(key)->first); + return find(key) == end() ? 0 : 1; } const_iterator find(const key_type& key) const { - return const_iterator(elements_.find(key)); + return old_style_ ? const_iterator(deprecated_elements_->find(key)) + : const_iterator(iterator(elements_->find(key))); } iterator find(const key_type& key) { - return iterator(elements_.find(key)); + return old_style_ ? iterator(deprecated_elements_->find(key)) + : iterator(elements_->find(key)); } std::pair equal_range( const key_type& key) const { @@ -724,13 +1557,22 @@ class Map { // insert std::pair insert(const value_type& value) { - iterator it = find(value.first); - if (it != end()) { - return std::pair(it, false); + if (old_style_) { + iterator it = find(value.first); + if (it != end()) { + return std::pair(it, false); + } else { + return std::pair( + iterator(deprecated_elements_->insert(std::pair( + value.first, CreateValueTypeInternal(value))).first), true); + } } else { - return std::pair( - iterator(elements_.insert(std::pair( - value.first, CreateValueTypeInternal(value))).first), true); + std::pair p = + elements_->insert(value.first); + if (p.second) { + p.first->value() = CreateValueTypeInternal(value); + } + return std::pair(iterator(p.first), p.second); } } template @@ -743,33 +1585,31 @@ class Map { } } - // Erase + // Erase and clear size_type erase(const key_type& key) { - typename InnerMap::iterator it = elements_.find(key); - if (it == elements_.end()) { + iterator it = find(key); + if (it == end()) { return 0; } else { - if (arena_ == NULL) delete it->second; - elements_.erase(it); + erase(it); return 1; } } - void erase(iterator pos) { - if (arena_ == NULL) delete pos.it_->second; - elements_.erase(pos.it_); + iterator erase(iterator pos) { + if (arena_ == NULL) delete pos.operator->(); + iterator i = pos++; + if (old_style_) + deprecated_elements_->erase(i.dit_); + else + elements_->erase(i.it_); + return pos; } void erase(iterator first, iterator last) { - for (iterator it = first; it != last;) { - if (arena_ == NULL) delete it.it_->second; - elements_.erase((it++).it_); + while (first != last) { + first = erase(first); } } - void clear() { - for (iterator it = begin(); it != end(); ++it) { - if (arena_ == NULL) delete it.it_->second; - } - elements_.clear(); - } + void clear() { erase(begin(), end()); } // Assign Map& operator=(const Map& other) { @@ -780,6 +1620,13 @@ class Map { return *this; } + // Access to hasher. Currently this returns a copy, but it may + // be modified to return a const reference in the future. + hasher hash_function() const { + return old_style_ ? deprecated_elements_->hash_function() + : elements_->hash_function(); + } + private: // Set default enum value only for proto2 map field whose value is enum type. void SetDefaultEnumValue(int default_enum_value) { @@ -814,9 +1661,15 @@ class Map { } Arena* arena_; - Allocator allocator_; - InnerMap elements_; int default_enum_value_; + // The following is a tagged union because we support two map styles + // for now. + // TODO(gpike): get rid of the old style. + const bool old_style_; + union { + InnerMap* elements_; + DeprecatedInnerMap* deprecated_elements_; + }; friend class ::google::protobuf::Arena; typedef void InternalArenaConstructable_; @@ -837,6 +1690,12 @@ struct hash { size_t operator()(const google::protobuf::MapKey& map_key) const { switch (map_key.type()) { + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: + case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: + GOOGLE_LOG(FATAL) << "Unsupported"; + break; case google::protobuf::FieldDescriptor::CPPTYPE_STRING: return hash()(map_key.GetStringValue()); case google::protobuf::FieldDescriptor::CPPTYPE_INT64: @@ -849,11 +1708,6 @@ struct hash { return hash< ::google::protobuf::uint32>()(map_key.GetUInt32Value()); case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: return hash()(map_key.GetBoolValue()); - case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: - case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: - case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: - case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: - GOOGLE_LOG(FATAL) << "Can't get here."; } GOOGLE_LOG(FATAL) << "Can't get here."; return 0; @@ -861,26 +1715,7 @@ struct hash { bool operator()(const google::protobuf::MapKey& map_key1, const google::protobuf::MapKey& map_key2) const { - switch (map_key1.type()) { -#define COMPARE_CPPTYPE(CPPTYPE, CPPTYPE_METHOD) \ - case google::protobuf::FieldDescriptor::CPPTYPE_##CPPTYPE: \ - return map_key1.Get##CPPTYPE_METHOD##Value() < \ - map_key2.Get##CPPTYPE_METHOD##Value(); - COMPARE_CPPTYPE(STRING, String) - COMPARE_CPPTYPE(INT64, Int64) - COMPARE_CPPTYPE(INT32, Int32) - COMPARE_CPPTYPE(UINT64, UInt64) - COMPARE_CPPTYPE(UINT32, UInt32) - COMPARE_CPPTYPE(BOOL, Bool) -#undef COMPARE_CPPTYPE - case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: - case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: - case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: - case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: - GOOGLE_LOG(FATAL) << "Can't get here."; - } - GOOGLE_LOG(FATAL) << "Can't get here."; - return true; + return map_key1 < map_key2; } }; GOOGLE_PROTOBUF_HASH_NAMESPACE_DECLARATION_END diff --git a/src/google/protobuf/map_field.cc b/src/google/protobuf/map_field.cc index eddc95c4c..49f918181 100644 --- a/src/google/protobuf/map_field.cc +++ b/src/google/protobuf/map_field.cc @@ -178,18 +178,19 @@ bool DynamicMapField::ContainsMapKey( return iter != map.end(); } -bool DynamicMapField::InsertMapValue( +bool DynamicMapField::InsertOrLookupMapValue( const MapKey& map_key, MapValueRef* val) { - bool result = false; - - MapValueRef& map_val = (*MutableMap())[map_key]; - // If map_val.data_ is not set, it is newly inserted by map_[map_key]. - if (map_val.data_ == NULL) { - result = true; + // Always use mutable map because users may change the map value by + // MapValueRef. + Map* map = MutableMap(); + Map::iterator iter = map->find(map_key); + if (iter == map->end()) { + // Insert + MapValueRef& map_val = (*map)[map_key]; const FieldDescriptor* val_des = default_entry_->GetDescriptor()->FindFieldByName("value"); map_val.SetType(val_des->cpp_type()); - // Allocate momery for the inserted MapValueRef, and initialize to + // Allocate memory for the inserted MapValueRef, and initialize to // default value. switch (val_des->cpp_type()) { #define HANDLE_TYPE(CPPTYPE, TYPE) \ @@ -216,9 +217,13 @@ bool DynamicMapField::InsertMapValue( break; } } + val->CopyFrom(map_val); + return true; } - val->CopyFrom(map_val); - return result; + // map_key is already in the map. Make sure (*map)[map_key] is not called. + // [] may reorder the map and iterators. + val->CopyFrom(iter->second); + return false; } bool DynamicMapField::DeleteMapValue(const MapKey& map_key) { diff --git a/src/google/protobuf/map_field.h b/src/google/protobuf/map_field.h index 9130166b8..4b46f3aa0 100644 --- a/src/google/protobuf/map_field.h +++ b/src/google/protobuf/map_field.h @@ -87,7 +87,8 @@ class LIBPROTOBUF_EXPORT MapFieldBase { // Pure virtual map APIs for Map Reflection. virtual bool ContainsMapKey(const MapKey& map_key) const = 0; - virtual bool InsertMapValue(const MapKey& map_key, MapValueRef* val) = 0; + virtual bool InsertOrLookupMapValue( + const MapKey& map_key, MapValueRef* val) = 0; virtual bool DeleteMapValue(const MapKey& map_key) = 0; virtual bool EqualIterator(const MapIterator& a, const MapIterator& b) const = 0; @@ -251,7 +252,7 @@ class MapField : public TypeDefinedMapFieldBase, // Implement MapFieldBase bool ContainsMapKey(const MapKey& map_key) const; - bool InsertMapValue(const MapKey& map_key, MapValueRef* val); + bool InsertOrLookupMapValue(const MapKey& map_key, MapValueRef* val); bool DeleteMapValue(const MapKey& map_key); // Accessors @@ -302,7 +303,7 @@ class LIBPROTOBUF_EXPORT DynamicMapField: public TypeDefinedMapFieldBase& GetMap() const; diff --git a/src/google/protobuf/map_field_inl.h b/src/google/protobuf/map_field_inl.h index f116697c4..01c9b89aa 100644 --- a/src/google/protobuf/map_field_inl.h +++ b/src/google/protobuf/map_field_inl.h @@ -262,16 +262,22 @@ template bool MapField::InsertMapValue(const MapKey& map_key, - MapValueRef* val) { + default_enum_value>::InsertOrLookupMapValue( + const MapKey& map_key, + MapValueRef* val) { + // Always use mutable map because users may change the map value by + // MapValueRef. Map* map = MutableMap(); - bool result = false; const Key& key = UnwrapMapKey(map_key); - if (map->end() == map->find(key)) { - result = true; + typename Map::iterator iter = map->find(key); + if (map->end() == iter) { + val->SetValue(&((*map)[key])); + return true; } - val->SetValue(&((*map)[key])); - return result; + // Key is already in the map. Make sure (*map)[key] is not called. + // [] may reorder the map and iterators. + val->SetValue(&(iter->second)); + return false; } template +#if defined(_WIN32) +#define _WINSOCKAPI_ // to avoid re-definition in WinSock2.h +#define NOMINMAX // to avoid defining min/max macros +#include +#endif // _WIN32 + +#include +#include #include #include #ifndef _SHARED_PTR_H #include #endif +#include #include +#include #include #include @@ -62,6 +74,7 @@ #include #include #include +#include #include #include #include @@ -79,10 +92,11 @@ namespace internal { // Map API Test ===================================================== -class MapImplTest : public ::testing::Test { +// Parameterized tests on whether to use old style maps. +class MapImplTest : public testing::TestWithParam { protected: MapImplTest() - : map_ptr_(new Map), + : map_ptr_(new Map(GetParam())), map_(*map_ptr_), const_map_(*map_ptr_) { EXPECT_TRUE(map_.empty()); @@ -159,7 +173,7 @@ class MapImplTest : public ::testing::Test { const Map& const_map_; }; -TEST_F(MapImplTest, OperatorBracket) { +TEST_P(MapImplTest, OperatorBracket) { int32 key = 0; int32 value1 = 100; int32 value2 = 101; @@ -173,7 +187,7 @@ TEST_F(MapImplTest, OperatorBracket) { ExpectSingleElement(key, value2); } -TEST_F(MapImplTest, OperatorBracketNonExist) { +TEST_P(MapImplTest, OperatorBracketNonExist) { int32 key = 0; int32 default_value = 0; @@ -181,7 +195,7 @@ TEST_F(MapImplTest, OperatorBracketNonExist) { ExpectSingleElement(key, default_value); } -TEST_F(MapImplTest, MutableAt) { +TEST_P(MapImplTest, MutableAt) { int32 key = 0; int32 value1 = 100; int32 value2 = 101; @@ -195,15 +209,15 @@ TEST_F(MapImplTest, MutableAt) { #ifdef PROTOBUF_HAS_DEATH_TEST -TEST_F(MapImplTest, MutableAtNonExistDeathTest) { +TEST_P(MapImplTest, MutableAtNonExistDeathTest) { EXPECT_DEATH(map_.at(0), ""); } -TEST_F(MapImplTest, ImmutableAtNonExistDeathTest) { +TEST_P(MapImplTest, ImmutableAtNonExistDeathTest) { EXPECT_DEATH(const_map_.at(0), ""); } -TEST_F(MapImplTest, UsageErrors) { +TEST_P(MapImplTest, UsageErrors) { MapKey key; key.SetInt64Value(1); EXPECT_DEATH(key.GetUInt64Value(), @@ -220,23 +234,23 @@ TEST_F(MapImplTest, UsageErrors) { #endif // PROTOBUF_HAS_DEATH_TEST -TEST_F(MapImplTest, CountNonExist) { +TEST_P(MapImplTest, CountNonExist) { EXPECT_EQ(0, map_.count(0)); } -TEST_F(MapImplTest, MutableFindNonExist) { +TEST_P(MapImplTest, MutableFindNonExist) { EXPECT_TRUE(map_.end() == map_.find(0)); } -TEST_F(MapImplTest, ImmutableFindNonExist) { +TEST_P(MapImplTest, ImmutableFindNonExist) { EXPECT_TRUE(const_map_.end() == const_map_.find(0)); } -TEST_F(MapImplTest, ConstEnd) { +TEST_P(MapImplTest, ConstEnd) { EXPECT_TRUE(const_map_.end() == const_map_.cend()); } -TEST_F(MapImplTest, GetReferenceFromIterator) { +TEST_P(MapImplTest, GetReferenceFromIterator) { for (int i = 0; i < 10; i++) { map_[i] = i; } @@ -259,7 +273,7 @@ TEST_F(MapImplTest, GetReferenceFromIterator) { } } -TEST_F(MapImplTest, IteratorBasic) { +TEST_P(MapImplTest, IteratorBasic) { map_[0] = 0; // Default constructible (per forward iterator requirements). @@ -281,6 +295,292 @@ TEST_F(MapImplTest, IteratorBasic) { EXPECT_TRUE(it == cit); } +template +static int64 median(Iterator i0, Iterator i1) { + vector v(i0, i1); + std::nth_element(v.begin(), v.begin() + v.size() / 2, v.end()); + return v[v.size() / 2]; +} + +static int64 Now() { + return google::protobuf::util::TimeUtil::TimestampToNanoseconds( + google::protobuf::util::TimeUtil::GetCurrentTime()); +} + +// Arbitrary odd integers for creating test data. +static int k0 = 812398771; +static int k1 = 1312938717; +static int k2 = 1321555333; + +// A naive begin() implementation will cause begin() to get slower and slower +// if one erases elements at the "front" of the hash map, and we'd like to +// avoid that, as std::unordered_map does. +TEST_P(MapImplTest, BeginIsFast) { + // Disable this test for both new and old implementations. + if (/*GetParam()*/true) return; + Map map(false); // This test uses new-style maps only. + const int kTestSize = 250000; + // Create a random-looking map of size n. Use non-negative integer keys. + uint32 frog = 123983; + int last_key = 0; + int counter = 0; + while (map.size() < kTestSize) { + frog *= static_cast(k0); + frog ^= frog >> 17; + frog += counter++; + last_key = + static_cast(frog) >= 0 ? static_cast(frog) : last_key ^ 1; + GOOGLE_DCHECK_GE(last_key, 0); + map[last_key] = last_key ^ 1; + } + vector times; + // We're going to do map.erase(map.begin()) over and over again. But, + // just in case one iteration is fast compared to the granularity of + // our time keeping, we measure kChunkSize iterations per outer-loop iter. + const int kChunkSize = 1000; + GOOGLE_CHECK_EQ(kTestSize % kChunkSize, 0); + do { + const int64 start = Now(); + for (int i = 0; i < kChunkSize; i++) { + map.erase(map.begin()); + } + const int64 end = Now(); + if (end > start) { + times.push_back(end - start); + } + } while (!map.empty()); + if (times.size() < .99 * kTestSize / kChunkSize) { + GOOGLE_LOG(WARNING) << "Now() isn't helping us measure time"; + return; + } + int64 x0 = median(times.begin(), times.begin() + 9); + int64 x1 = median(times.begin() + times.size() - 9, times.end()); + GOOGLE_LOG(INFO) << "x0=" << x0 << ", x1=" << x1; + // x1 will greatly exceed x0 if the code we just executed took O(n^2) time. + // And we'll probably time out and never get here. So, this test is + // intentionally loose: we check that x0 and x1 are within a factor of 8. + EXPECT_GE(x1, x0 / 8); + EXPECT_GE(x0, x1 / 8); +} + +// Try to create kTestSize keys that will land in just a few buckets, and +// time the insertions, to get a rough estimate of whether an O(n^2) worst case +// was triggered. This test is a hacky, but probably better than nothing. +TEST_P(MapImplTest, HashFlood) { + const int kTestSize = 1024; // must be a power of 2 + std::set s; + for (int i = 0; s.size() < kTestSize; i++) { + if ((map_.hash_function()(i) & (kTestSize - 1)) < 3) { + s.insert(i); + } + } + // Create hash table with kTestSize entries that hash flood a table with + // 1024 (or 512 or 2048 or ...) entries. This assumes that map_ uses powers + // of 2 for table sizes, and that it's sufficient to "flood" with respect to + // the low bits of the output of map_.hash_function(). + vector times; + std::set::iterator it = s.begin(); + int count = 0; + do { + const int64 start = Now(); + map_[*it] = 0; + const int64 end = Now(); + if (end > start) { + times.push_back(end - start); + } + ++count; + ++it; + } while (it != s.end()); + if (times.size() < .99 * count) return; + int64 x0 = median(times.begin(), times.begin() + 9); + int64 x1 = median(times.begin() + times.size() - 9, times.end()); + // x1 will greatly exceed x0 if the code we just executed took O(n^2) time. + // But we want to allow O(n log n). A factor of 20 should be generous enough. + EXPECT_LE(x1, x0 * 20); +} + +template +static void TestValidityForAllKeysExcept(int key_to_avoid, + const T& check_map, + const U& map) { + typedef typename U::value_type value_type; // a key-value pair + for (typename U::const_iterator it = map.begin(); it != map.end(); ++it) { + const int key = it->first; + if (key == key_to_avoid) continue; + // All iterators relevant to this key, whether old (from check_map) or new, + // must point to the same memory. So, test pointer equality here. + const value_type* check_val = &*check_map.find(key)->second; + EXPECT_EQ(check_val, &*it); + EXPECT_EQ(check_val, &*map.find(key)); + } +} + +// EXPECT i0 and i1 to be the same. Advancing them should have the same effect, +// too. +template +static void TestEqualIterators(Iter i0, Iter i1, Iter end) { + const int kMaxAdvance = 10; + for (int i = 0; i < kMaxAdvance; i++) { + EXPECT_EQ(i0 == end, i1 == end); + if (i0 == end) return; + EXPECT_EQ(&*i0, &*i1) << "iter " << i; + ++i0; + ++i1; + } +} + +template +static void TestOldVersusNewIterator(int skip, Map* m) { + const int initial_size = m->size(); + IteratorType it = m->begin(); + for (int i = 0; i < skip && it != m->end(); it++, i++) {} + if (it == m->end()) return; + const IteratorType old = it; + GOOGLE_LOG(INFO) << "skip=" << skip << ", old->first=" << old->first; + const int target_size = + initial_size < 100 ? initial_size * 5 : initial_size * 5 / 4; + for (int i = 0; m->size() <= target_size; i++) { + (*m)[i] = 0; + } + // Iterator 'old' should still work just fine despite the growth of *m. + const IteratorType after_growth = m->find(old->first); + TestEqualIterators(old, after_growth, m->end()); + + // Now shrink the number of elements. Do this with a mix of erases and + // inserts to increase the chance that the hashtable will resize to a lower + // number of buckets. (But, in any case, the test is still useful.) + for (int i = 0; i < 2 * (target_size - initial_size); i++) { + if (i != old->first) { + m->erase(i); + } + if (((i ^ m->begin()->first) & 15) == 0) { + (*m)[i * 342] = i; + } + } + // Now, the table has grown and shrunk; test again. + TestEqualIterators(old, m->find(old->first), m->end()); + TestEqualIterators(old, after_growth, m->end()); +} + +// Create and test an n-element Map, with emphasis on iterator correctness. +static void StressTestIterators(int n, bool test_old_style_proto2_maps) { + GOOGLE_LOG(INFO) << "StressTestIterators " << n; + GOOGLE_CHECK_GT(n, 0); + // Create a random-looking map of size n. Use non-negative integer keys. + Map m(test_old_style_proto2_maps); + uint32 frog = 123987 + n; + int last_key = 0; + int counter = 0; + while (m.size() < n) { + frog *= static_cast(k0); + frog ^= frog >> 17; + frog += counter++; + last_key = + static_cast(frog) >= 0 ? static_cast(frog) : last_key ^ 1; + GOOGLE_DCHECK_GE(last_key, 0); + m[last_key] = last_key ^ 1; + } + // Test it. + ASSERT_EQ(n, m.size()); + // Create maps of pointers and iterators. + // These should remain valid even if we modify m. + hash_map::value_type*> mp(n); + hash_map::iterator> mi(n); + for (Map::iterator it = m.begin(); it != m.end(); ++it) { + mp[it->first] = &*it; + mi[it->first] = it; + } + ASSERT_EQ(m.size(), mi.size()); + ASSERT_EQ(m.size(), mp.size()); + m.erase(last_key); + ASSERT_EQ(n - 1, m.size()); + TestValidityForAllKeysExcept(last_key, mp, m); + TestValidityForAllKeysExcept(last_key, mi, m); + + m[last_key] = 0; + ASSERT_EQ(n, m.size()); + // Test old iterator vs new iterator, with table modification in between. + TestOldVersusNewIterator::const_iterator>(n % 3, &m); + TestOldVersusNewIterator::iterator>(n % (1 + (n / 40)), &m); + // Finally, ensure erase(iterator) doesn't reorder anything, becuase that is + // what its documentation says. + m[last_key] = m[last_key ^ 999] = 0; + vector::iterator> v; + v.reserve(m.size()); + int position_of_last_key = 0; + for (Map::iterator it = m.begin(); it != m.end(); ++it) { + if (it->first == last_key) { + position_of_last_key = v.size(); + } + v.push_back(it); + } + ASSERT_EQ(m.size(), v.size()); + const Map::iterator erase_result = m.erase(m.find(last_key)); + int index = 0; + for (Map::iterator it = m.begin(); it != m.end(); ++it, ++index) { + if (index == position_of_last_key) { + EXPECT_EQ(&*erase_result, &*v[++index]); + } + ASSERT_EQ(&*it, &*v[index]); + } +} + +TEST_P(MapImplTest, IteratorInvalidation) { + // As multiple underlying hash_map implementations do not follow the + // validation requirement, the test is disabled for old-style maps. + if (GetParam()) return; + // Create a set of pseudo-random sizes to test. +#ifndef NDEBUG + const int kMaxSizeToTest = 100 * 1000; +#else + const int kMaxSizeToTest = 1000 * 1000; +#endif + std::set s; + int n = kMaxSizeToTest; + int frog = k1 + n; + while (n > 1 && s.size() < 25) { + s.insert(n); + n = static_cast(n * 100 / (101.0 + (frog & 63))); + frog *= k2; + frog ^= frog >> 17; + } + // Ensure we test a few small sizes. + s.insert(1); + s.insert(2); + s.insert(3); + // Now, the real work. + for (std::set::iterator i = s.begin(); i != s.end(); ++i) { + StressTestIterators(*i, GetParam()); + } +} + +// Test that erase() revalidates iterators. +TEST_P(MapImplTest, EraseRevalidates) { + // As multiple underlying hash_map implementations do not follow the + // validation requirement, the test is disabled for old-style maps. + if (GetParam()) return; + map_[3] = map_[13] = map_[20] = 0; + const int initial_size = map_.size(); + EXPECT_EQ(3, initial_size); + vector::iterator> v; + for (Map::iterator it = map_.begin(); it != map_.end(); ++it) { + v.push_back(it); + } + EXPECT_EQ(initial_size, v.size()); + for (int i = 0; map_.size() <= initial_size * 20; i++) { + map_[i] = 0; + } + const int larger_size = map_.size(); + // We've greatly increased the size of the map, so it is highly likely that + // the following will corrupt m if erase() doesn't properly revalidate + // iterators passed to it. Finishing this routine without crashing indicates + // success. + for (int i = 0; i < v.size(); i++) { + map_.erase(v[i]); + } + EXPECT_EQ(larger_size - v.size(), map_.size()); +} + template bool IsConstHelper(T& /*t*/) { // NOLINT. We want to catch non-const refs here. return false; @@ -290,7 +590,7 @@ bool IsConstHelper(const T& /*t*/) { return true; } -TEST_F(MapImplTest, IteratorConstness) { +TEST_P(MapImplTest, IteratorConstness) { map_[0] = 0; EXPECT_TRUE(IsConstHelper(*map_.cbegin())); EXPECT_TRUE(IsConstHelper(*const_map_.begin())); @@ -303,14 +603,14 @@ bool IsForwardIteratorHelper(T /*t*/) { return false; } -TEST_F(MapImplTest, IteratorCategory) { +TEST_P(MapImplTest, IteratorCategory) { EXPECT_TRUE(IsForwardIteratorHelper( std::iterator_traits::iterator>::iterator_category())); EXPECT_TRUE(IsForwardIteratorHelper(std::iterator_traits< Map::const_iterator>::iterator_category())); } -TEST_F(MapImplTest, InsertSingle) { +TEST_P(MapImplTest, InsertSingle) { int32 key = 0; int32 value1 = 100; int32 value2 = 101; @@ -335,7 +635,7 @@ TEST_F(MapImplTest, InsertSingle) { EXPECT_FALSE(result2.second); } -TEST_F(MapImplTest, InsertByIterator) { +TEST_P(MapImplTest, InsertByIterator) { int32 key1 = 0; int32 key2 = 1; int32 value1a = 100; @@ -358,7 +658,7 @@ TEST_F(MapImplTest, InsertByIterator) { ExpectElements(map1); } -TEST_F(MapImplTest, EraseSingleByKey) { +TEST_P(MapImplTest, EraseSingleByKey) { int32 key = 0; int32 value = 100; @@ -376,7 +676,7 @@ TEST_F(MapImplTest, EraseSingleByKey) { EXPECT_EQ(0, map_.erase(key)); } -TEST_F(MapImplTest, EraseMutipleByKey) { +TEST_P(MapImplTest, EraseMutipleByKey) { // erase in one specific order to trigger corner cases for (int i = 0; i < 5; i++) { map_[i] = i; @@ -403,7 +703,7 @@ TEST_F(MapImplTest, EraseMutipleByKey) { EXPECT_TRUE(map_.end() == map_.find(2)); } -TEST_F(MapImplTest, EraseSingleByIterator) { +TEST_P(MapImplTest, EraseSingleByIterator) { int32 key = 0; int32 value = 100; @@ -418,7 +718,7 @@ TEST_F(MapImplTest, EraseSingleByIterator) { EXPECT_TRUE(map_.begin() == map_.end()); } -TEST_F(MapImplTest, ValidIteratorAfterErase) { +TEST_P(MapImplTest, ValidIteratorAfterErase) { for (int i = 0; i < 10; i++) { map_[i] = i; } @@ -438,7 +738,7 @@ TEST_F(MapImplTest, ValidIteratorAfterErase) { EXPECT_EQ(5, map_.size()); } -TEST_F(MapImplTest, EraseByIterator) { +TEST_P(MapImplTest, EraseByIterator) { int32 key1 = 0; int32 key2 = 1; int32 value1 = 100; @@ -459,7 +759,7 @@ TEST_F(MapImplTest, EraseByIterator) { EXPECT_TRUE(map_.begin() == map_.end()); } -TEST_F(MapImplTest, Clear) { +TEST_P(MapImplTest, Clear) { int32 key = 0; int32 value = 100; @@ -474,7 +774,7 @@ TEST_F(MapImplTest, Clear) { EXPECT_TRUE(map_.begin() == map_.end()); } -TEST_F(MapImplTest, CopyConstructor) { +static void CopyConstructorHelper(Arena* arena, Map* m) { int32 key1 = 0; int32 key2 = 1; int32 value1 = 100; @@ -484,16 +784,25 @@ TEST_F(MapImplTest, CopyConstructor) { map[key1] = value1; map[key2] = value2; - map_.insert(map.begin(), map.end()); + m->insert(map.begin(), map.end()); - Map other(map_); + Map other(*m); EXPECT_EQ(2, other.size()); EXPECT_EQ(value1, other.at(key1)); EXPECT_EQ(value2, other.at(key2)); } -TEST_F(MapImplTest, IterConstructor) { +TEST_P(MapImplTest, CopyConstructorWithArena) { + Arena a; + CopyConstructorHelper(&a, &map_); +} + +TEST_P(MapImplTest, CopyConstructorWithoutArena) { + CopyConstructorHelper(NULL, &map_); +} + +TEST_P(MapImplTest, IterConstructor) { int32 key1 = 0; int32 key2 = 1; int32 value1 = 100; @@ -503,14 +812,15 @@ TEST_F(MapImplTest, IterConstructor) { map[key1] = value1; map[key2] = value2; - Map new_map(map.begin(), map.end()); + Map new_map(map.begin(), map.end(), + GetParam()); EXPECT_EQ(2, new_map.size()); EXPECT_EQ(value1, new_map.at(key1)); EXPECT_EQ(value2, new_map.at(key2)); } -TEST_F(MapImplTest, Assigner) { +TEST_P(MapImplTest, Assigner) { int32 key1 = 0; int32 key2 = 1; int32 value1 = 100; @@ -522,7 +832,7 @@ TEST_F(MapImplTest, Assigner) { map_.insert(map.begin(), map.end()); - Map other; + Map other(GetParam()); int32 key_other = 123; int32 value_other = 321; other[key_other] = value_other; @@ -540,9 +850,16 @@ TEST_F(MapImplTest, Assigner) { EXPECT_EQ(2, other.size()); EXPECT_EQ(value1, other.at(key1)); EXPECT_EQ(value2, other.at(key2)); + + // Try assignment to a map with a different choice of "style." + Map m(!GetParam()); + m = other; + EXPECT_EQ(2, m.size()); + EXPECT_EQ(value1, m.at(key1)); + EXPECT_EQ(value2, m.at(key2)); } -TEST_F(MapImplTest, Rehash) { +TEST_P(MapImplTest, Rehash) { const int test_size = 50; std::map reference_map; for (int i = 0; i < test_size; i++) { @@ -559,7 +876,7 @@ TEST_F(MapImplTest, Rehash) { EXPECT_TRUE(map_.empty()); } -TEST_F(MapImplTest, EqualRange) { +TEST_P(MapImplTest, EqualRange) { int key = 100, key_missing = 101; map_[key] = 100; @@ -583,14 +900,14 @@ TEST_F(MapImplTest, EqualRange) { EXPECT_TRUE(const_map_.end() == const_range.second); } -TEST_F(MapImplTest, ConvertToStdMap) { +TEST_P(MapImplTest, ConvertToStdMap) { map_[100] = 101; std::map std_map(map_.begin(), map_.end()); EXPECT_EQ(1, std_map.size()); EXPECT_EQ(101, std_map[100]); } -TEST_F(MapImplTest, ConvertToStdVectorOfPairs) { +TEST_P(MapImplTest, ConvertToStdVectorOfPairs) { map_[100] = 101; std::vector > std_vec(map_.begin(), map_.end()); EXPECT_EQ(1, std_vec.size()); @@ -598,6 +915,8 @@ TEST_F(MapImplTest, ConvertToStdVectorOfPairs) { EXPECT_EQ(101, std_vec[0].second); } +INSTANTIATE_TEST_CASE_P(BoolSequence, MapImplTest, testing::Bool()); + // Map Field Reflection Test ======================================== static int Func(int i, int j) { diff --git a/src/google/protobuf/message.cc b/src/google/protobuf/message.cc index 032748bda..d62ca79c8 100644 --- a/src/google/protobuf/message.cc +++ b/src/google/protobuf/message.cc @@ -69,7 +69,7 @@ void Message::MergeFrom(const Message& from) { GOOGLE_CHECK_EQ(from.GetDescriptor(), descriptor) << ": Tried to merge from a message with a different type. " "to: " << descriptor->full_name() << ", " - "from:" << from.GetDescriptor()->full_name(); + "from: " << from.GetDescriptor()->full_name(); ReflectionOps::Merge(from, this); } @@ -82,7 +82,7 @@ void Message::CopyFrom(const Message& from) { GOOGLE_CHECK_EQ(from.GetDescriptor(), descriptor) << ": Tried to copy from a message with a different type. " "to: " << descriptor->full_name() << ", " - "from:" << from.GetDescriptor()->full_name(); + "from: " << from.GetDescriptor()->full_name(); ReflectionOps::Copy(from, this); } diff --git a/src/google/protobuf/message_lite.cc b/src/google/protobuf/message_lite.cc index 5bd8bcfb1..9d7b64f74 100644 --- a/src/google/protobuf/message_lite.cc +++ b/src/google/protobuf/message_lite.cc @@ -62,13 +62,15 @@ namespace { // provide a useful error message. void ByteSizeConsistencyError(int byte_size_before_serialization, int byte_size_after_serialization, - int bytes_produced_by_serialization) { + int bytes_produced_by_serialization, + const MessageLite& message) { GOOGLE_CHECK_EQ(byte_size_before_serialization, byte_size_after_serialization) - << "Protocol message was modified concurrently during serialization."; + << message.GetTypeName() + << " was modified concurrently during serialization."; GOOGLE_CHECK_EQ(bytes_produced_by_serialization, byte_size_before_serialization) << "Byte size calculation and serialization were inconsistent. This " "may indicate a bug in protocol buffers or it may be caused by " - "concurrent modification of the message."; + "concurrent modification of " << message.GetTypeName() << "."; GOOGLE_LOG(FATAL) << "This shouldn't be called if all the sizes are equal."; } @@ -248,7 +250,7 @@ bool MessageLite::SerializePartialToCodedStream( if (buffer != NULL) { uint8* end = SerializeWithCachedSizesToArray(buffer); if (end - buffer != size) { - ByteSizeConsistencyError(size, ByteSize(), end - buffer); + ByteSizeConsistencyError(size, ByteSize(), end - buffer, *this); } return true; } else { @@ -261,7 +263,7 @@ bool MessageLite::SerializePartialToCodedStream( if (final_byte_count - original_byte_count != size) { ByteSizeConsistencyError(size, ByteSize(), - final_byte_count - original_byte_count); + final_byte_count - original_byte_count, *this); } return true; @@ -299,7 +301,7 @@ bool MessageLite::AppendPartialToString(string* output) const { reinterpret_cast(io::mutable_string_data(output) + old_size); uint8* end = SerializeWithCachedSizesToArray(start); if (end - start != byte_size) { - ByteSizeConsistencyError(byte_size, ByteSize(), end - start); + ByteSizeConsistencyError(byte_size, ByteSize(), end - start, *this); } return true; } @@ -325,7 +327,7 @@ bool MessageLite::SerializePartialToArray(void* data, int size) const { uint8* start = reinterpret_cast(data); uint8* end = SerializeWithCachedSizesToArray(start); if (end - start != byte_size) { - ByteSizeConsistencyError(byte_size, ByteSize(), end - start); + ByteSizeConsistencyError(byte_size, ByteSize(), end - start, *this); } return true; } @@ -359,6 +361,11 @@ void GenericTypeHandler::Merge(const MessageLite& from, MessageLite* to) { to->CheckTypeAndMergeFrom(from); } +template<> +void GenericTypeHandler::Merge(const string& from, + string* to) { + *to = from; +} } // namespace internal } // namespace protobuf diff --git a/src/google/protobuf/message_unittest.cc b/src/google/protobuf/message_unittest.cc index 2d4780fe6..d668a1a6b 100644 --- a/src/google/protobuf/message_unittest.cc +++ b/src/google/protobuf/message_unittest.cc @@ -55,6 +55,7 @@ #include #include +#include #include #include diff --git a/src/google/protobuf/metadata.h b/src/google/protobuf/metadata.h index 219645d3a..fdee150b4 100644 --- a/src/google/protobuf/metadata.h +++ b/src/google/protobuf/metadata.h @@ -56,7 +56,7 @@ namespace internal { // The tagged pointer uses the LSB to disambiguate cases, and uses bit 0 == 0 to // indicate an arena pointer and bit 0 == 1 to indicate a UFS+Arena-container // pointer. -class InternalMetadataWithArena { +class LIBPROTOBUF_EXPORT InternalMetadataWithArena { public: InternalMetadataWithArena() : ptr_(NULL) {} explicit InternalMetadataWithArena(Arena* arena) diff --git a/src/google/protobuf/proto3_arena_lite_unittest.cc b/src/google/protobuf/proto3_arena_lite_unittest.cc new file mode 100644 index 000000000..0f18c027e --- /dev/null +++ b/src/google/protobuf/proto3_arena_lite_unittest.cc @@ -0,0 +1,164 @@ +// 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 +#include +#ifndef _SHARED_PTR_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include + +namespace google { +using proto3_arena_lite_unittest::TestAllTypes; + +namespace protobuf { +namespace { +// We selectively set/check a few representative fields rather than all fields +// as this test is only expected to cover the basics of arena support. +void SetAllFields(TestAllTypes* m) { + m->set_optional_int32(100); + m->set_optional_string("asdf"); + m->set_optional_bytes("jkl;"); + m->mutable_optional_nested_message()->set_bb(42); + m->mutable_optional_foreign_message()->set_c(43); + m->set_optional_nested_enum( + proto3_arena_lite_unittest::TestAllTypes_NestedEnum_BAZ); + m->set_optional_foreign_enum( + proto3_arena_lite_unittest::FOREIGN_BAZ); + m->mutable_optional_lazy_message()->set_bb(45); + m->add_repeated_int32(100); + m->add_repeated_string("asdf"); + m->add_repeated_bytes("jkl;"); + m->add_repeated_nested_message()->set_bb(46); + m->add_repeated_foreign_message()->set_c(47); + m->add_repeated_nested_enum( + proto3_arena_lite_unittest::TestAllTypes_NestedEnum_BAZ); + m->add_repeated_foreign_enum( + proto3_arena_lite_unittest::FOREIGN_BAZ); + m->add_repeated_lazy_message()->set_bb(49); + + m->set_oneof_uint32(1); + m->mutable_oneof_nested_message()->set_bb(50); + m->set_oneof_string("test"); // only this one remains set +} + +void ExpectAllFieldsSet(const TestAllTypes& m) { + EXPECT_EQ(100, m.optional_int32()); + EXPECT_EQ("asdf", m.optional_string()); + EXPECT_EQ("jkl;", m.optional_bytes()); + EXPECT_EQ(true, m.has_optional_nested_message()); + EXPECT_EQ(42, m.optional_nested_message().bb()); + EXPECT_EQ(true, m.has_optional_foreign_message()); + EXPECT_EQ(43, m.optional_foreign_message().c()); + EXPECT_EQ(proto3_arena_lite_unittest::TestAllTypes_NestedEnum_BAZ, + m.optional_nested_enum()); + EXPECT_EQ(proto3_arena_lite_unittest::FOREIGN_BAZ, + m.optional_foreign_enum()); + EXPECT_EQ(true, m.has_optional_lazy_message()); + EXPECT_EQ(45, m.optional_lazy_message().bb()); + + EXPECT_EQ(1, m.repeated_int32_size()); + EXPECT_EQ(100, m.repeated_int32(0)); + EXPECT_EQ(1, m.repeated_string_size()); + EXPECT_EQ("asdf", m.repeated_string(0)); + EXPECT_EQ(1, m.repeated_bytes_size()); + EXPECT_EQ("jkl;", m.repeated_bytes(0)); + EXPECT_EQ(1, m.repeated_nested_message_size()); + EXPECT_EQ(46, m.repeated_nested_message(0).bb()); + EXPECT_EQ(1, m.repeated_foreign_message_size()); + EXPECT_EQ(47, m.repeated_foreign_message(0).c()); + EXPECT_EQ(1, m.repeated_nested_enum_size()); + EXPECT_EQ(proto3_arena_lite_unittest::TestAllTypes_NestedEnum_BAZ, + m.repeated_nested_enum(0)); + EXPECT_EQ(1, m.repeated_foreign_enum_size()); + EXPECT_EQ(proto3_arena_lite_unittest::FOREIGN_BAZ, + m.repeated_foreign_enum(0)); + EXPECT_EQ(1, m.repeated_lazy_message_size()); + EXPECT_EQ(49, m.repeated_lazy_message(0).bb()); + + EXPECT_EQ(proto3_arena_lite_unittest::TestAllTypes::kOneofString, + m.oneof_field_case()); + EXPECT_EQ("test", m.oneof_string()); +} + +// In this file we only test some basic functionalities of arena support in +// proto3 and expect the arena support to be fully tested in proto2 unittests +// because proto3 shares most code with proto2. + +TEST(Proto3ArenaLiteTest, Parsing) { + TestAllTypes original; + SetAllFields(&original); + + Arena arena; + TestAllTypes* arena_message = Arena::CreateMessage(&arena); + arena_message->ParseFromString(original.SerializeAsString()); + ExpectAllFieldsSet(*arena_message); +} + +TEST(Proto3ArenaLiteTest, Swap) { + Arena arena1; + Arena arena2; + + // Test Swap(). + TestAllTypes* arena1_message = Arena::CreateMessage(&arena1); + TestAllTypes* arena2_message = Arena::CreateMessage(&arena2); + arena1_message->Swap(arena2_message); + EXPECT_EQ(&arena1, arena1_message->GetArena()); + EXPECT_EQ(&arena2, arena2_message->GetArena()); +} + +TEST(Proto3ArenaLiteTest, SetAllocatedMessage) { + Arena arena; + TestAllTypes *arena_message = Arena::CreateMessage(&arena); + TestAllTypes::NestedMessage* nested = new TestAllTypes::NestedMessage; + nested->set_bb(118); + arena_message->set_allocated_optional_nested_message(nested); + EXPECT_EQ(118, arena_message->optional_nested_message().bb()); +} + +TEST(Proto3ArenaLiteTest, ReleaseMessage) { + Arena arena; + TestAllTypes* arena_message = Arena::CreateMessage(&arena); + arena_message->mutable_optional_nested_message()->set_bb(118); + google::protobuf::scoped_ptr nested( + arena_message->release_optional_nested_message()); + EXPECT_EQ(118, nested->bb()); +} + +} // namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/proto3_lite_unittest.cc b/src/google/protobuf/proto3_lite_unittest.cc new file mode 100644 index 000000000..2e2beea90 --- /dev/null +++ b/src/google/protobuf/proto3_lite_unittest.cc @@ -0,0 +1,145 @@ +// 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 +#include +#ifndef _SHARED_PTR_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include + +namespace google { +using proto3_lite_unittest::TestAllTypes; + +namespace protobuf { +namespace { +// We selectively set/check a few representative fields rather than all fields +// as this test is only expected to cover the basics of lite support. +void SetAllFields(TestAllTypes* m) { + m->set_optional_int32(100); + m->set_optional_string("asdf"); + m->set_optional_bytes("jkl;"); + m->mutable_optional_nested_message()->set_bb(42); + m->mutable_optional_foreign_message()->set_c(43); + m->set_optional_nested_enum( + proto3_lite_unittest::TestAllTypes_NestedEnum_BAZ); + m->set_optional_foreign_enum( + proto3_lite_unittest::FOREIGN_BAZ); + m->mutable_optional_lazy_message()->set_bb(45); + m->add_repeated_int32(100); + m->add_repeated_string("asdf"); + m->add_repeated_bytes("jkl;"); + m->add_repeated_nested_message()->set_bb(46); + m->add_repeated_foreign_message()->set_c(47); + m->add_repeated_nested_enum( + proto3_lite_unittest::TestAllTypes_NestedEnum_BAZ); + m->add_repeated_foreign_enum( + proto3_lite_unittest::FOREIGN_BAZ); + m->add_repeated_lazy_message()->set_bb(49); + + m->set_oneof_uint32(1); + m->mutable_oneof_nested_message()->set_bb(50); + m->set_oneof_string("test"); // only this one remains set +} + +void ExpectAllFieldsSet(const TestAllTypes& m) { + EXPECT_EQ(100, m.optional_int32()); + EXPECT_EQ("asdf", m.optional_string()); + EXPECT_EQ("jkl;", m.optional_bytes()); + EXPECT_EQ(true, m.has_optional_nested_message()); + EXPECT_EQ(42, m.optional_nested_message().bb()); + EXPECT_EQ(true, m.has_optional_foreign_message()); + EXPECT_EQ(43, m.optional_foreign_message().c()); + EXPECT_EQ(proto3_lite_unittest::TestAllTypes_NestedEnum_BAZ, + m.optional_nested_enum()); + EXPECT_EQ(proto3_lite_unittest::FOREIGN_BAZ, + m.optional_foreign_enum()); + EXPECT_EQ(true, m.has_optional_lazy_message()); + EXPECT_EQ(45, m.optional_lazy_message().bb()); + + EXPECT_EQ(1, m.repeated_int32_size()); + EXPECT_EQ(100, m.repeated_int32(0)); + EXPECT_EQ(1, m.repeated_string_size()); + EXPECT_EQ("asdf", m.repeated_string(0)); + EXPECT_EQ(1, m.repeated_bytes_size()); + EXPECT_EQ("jkl;", m.repeated_bytes(0)); + EXPECT_EQ(1, m.repeated_nested_message_size()); + EXPECT_EQ(46, m.repeated_nested_message(0).bb()); + EXPECT_EQ(1, m.repeated_foreign_message_size()); + EXPECT_EQ(47, m.repeated_foreign_message(0).c()); + EXPECT_EQ(1, m.repeated_nested_enum_size()); + EXPECT_EQ(proto3_lite_unittest::TestAllTypes_NestedEnum_BAZ, + m.repeated_nested_enum(0)); + EXPECT_EQ(1, m.repeated_foreign_enum_size()); + EXPECT_EQ(proto3_lite_unittest::FOREIGN_BAZ, + m.repeated_foreign_enum(0)); + EXPECT_EQ(1, m.repeated_lazy_message_size()); + EXPECT_EQ(49, m.repeated_lazy_message(0).bb()); + + EXPECT_EQ(proto3_lite_unittest::TestAllTypes::kOneofString, + m.oneof_field_case()); + EXPECT_EQ("test", m.oneof_string()); +} + +// In this file we only test some basic functionalities of in proto3 and expect +// the rest is fully tested in proto2 unittests because proto3 shares most code +// with proto2. + +TEST(Proto3LiteTest, Parsing) { + TestAllTypes original; + SetAllFields(&original); + + TestAllTypes msg; + msg.ParseFromString(original.SerializeAsString()); + ExpectAllFieldsSet(msg); +} + +TEST(Proto3LiteTest, Swap) { + // Test Swap(). + TestAllTypes msg1; + TestAllTypes msg2; + msg1.set_optional_string("123"); + msg2.set_optional_string("3456"); + msg1.Swap(&msg2); + EXPECT_EQ("3456", msg1.optional_string()); + EXPECT_EQ("123", msg2.optional_string()); + EXPECT_EQ(msg1.ByteSize(), msg2.ByteSize() + 1); +} + +} // namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/repeated_field.cc b/src/google/protobuf/repeated_field.cc index 949e0a235..77004f59a 100644 --- a/src/google/protobuf/repeated_field.cc +++ b/src/google/protobuf/repeated_field.cc @@ -52,8 +52,8 @@ void** RepeatedPtrFieldBase::InternalExtend(int extend_amount) { } Rep* old_rep = rep_; Arena* arena = GetArenaNoVirtual(); - new_size = max(kMinRepeatedFieldAllocationSize, - max(total_size_ * 2, new_size)); + new_size = std::max(kMinRepeatedFieldAllocationSize, + std::max(total_size_ * 2, new_size)); GOOGLE_CHECK_LE(new_size, (std::numeric_limits::max() - kRepHeaderSize) / sizeof(old_rep->elements[0])) diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h index 5447fa42a..1961bc481 100644 --- a/src/google/protobuf/repeated_field.h +++ b/src/google/protobuf/repeated_field.h @@ -244,7 +244,7 @@ class RepeatedField { int total_size_; struct Rep { Arena* arena; - Element elements[1]; + Element elements[1]; }; // We can not use sizeof(Rep) - sizeof(Element) due to the trailing padding on // the struct. We can not use sizeof(Arena*) as well because there might be @@ -272,6 +272,22 @@ class RepeatedField { inline Arena* GetArenaNoVirtual() const { return (rep_ == NULL) ? NULL : rep_->arena; } + + // Internal helper to delete all elements and deallocate the storage. + // If Element has a trivial destructor (for example, if it's a fundamental + // type, like int32), the loop will be removed by the optimizer. + void InternalDeallocate(Rep* rep, int size) { + if (rep != NULL) { + Element* e = &rep->elements[0]; + Element* limit = &rep->elements[size]; + for (; e < limit; e++) { + e->Element::~Element(); + } + if (rep->arena == NULL) { + delete[] reinterpret_cast(rep); + } + } + } }; template @@ -610,6 +626,13 @@ inline void* GenericTypeHandler::GetMaybeArenaPointer( template <> void GenericTypeHandler::Merge(const MessageLite& from, MessageLite* to); +template<> +inline void GenericTypeHandler::Clear(string* value) { + value->clear(); +} +template<> +void GenericTypeHandler::Merge(const string& from, + string* to); // Declarations of the specialization as we cannot define them here, as the // header that defines ProtocolMessage depends on types defined in this header. @@ -836,6 +859,15 @@ class RepeatedPtrField : public internal::RepeatedPtrFieldBase { // Add an already-allocated object, skipping arena-ownership checks. The user // must guarantee that the given object is in the same arena as this // RepeatedPtrField. + // It is also useful in legacy code that uses temporary ownership to avoid + // copies. Example: + // RepeatedPtrField temp_field; + // temp_field.AddAllocated(new T); + // ... // Do something with temp_field + // temp_field.ExtractSubrange(0, temp_field.size(), NULL); + // If you put temp_field on the arena this fails, because the ownership + // transfers to the arena at the "AddAllocated" call and is not released + // anymore causing a double delete. UnsafeArenaAddAllocated prevents this. void UnsafeArenaAddAllocated(Element* value); // Remove the last element and return it. Works only when operating on an @@ -992,19 +1024,8 @@ RepeatedField::RepeatedField(Iter begin, const Iter& end) template RepeatedField::~RepeatedField() { // See explanation in Reserve(): we need to invoke destructors here for the - // case that Element has a non-trivial destructor. If Element has a trivial - // destructor (for example, if it's a primitive type, like int32), this entire - // loop will be removed by the optimizer. - if (rep_ != NULL) { - Element* e = &rep_->elements[0]; - Element* limit = &rep_->elements[total_size_]; - for (; e < limit; e++) { - e->Element::~Element(); - } - if (rep_->arena == NULL) { - delete[] reinterpret_cast(rep_); - } - } + // case that Element has a non-trivial destructor. + InternalDeallocate(rep_, total_size_); } template @@ -1240,8 +1261,8 @@ void RepeatedField::Reserve(int new_size) { if (total_size_ >= new_size) return; Rep* old_rep = rep_; Arena* arena = GetArenaNoVirtual(); - new_size = max(google::protobuf::internal::kMinRepeatedFieldAllocationSize, - max(total_size_ * 2, new_size)); + new_size = std::max(google::protobuf::internal::kMinRepeatedFieldAllocationSize, + std::max(total_size_ * 2, new_size)); GOOGLE_CHECK_LE(static_cast(new_size), (std::numeric_limits::max() - kRepHeaderSize) / sizeof(Element)) @@ -1274,18 +1295,10 @@ void RepeatedField::Reserve(int new_size) { if (current_size_ > 0) { MoveArray(rep_->elements, old_rep->elements, current_size_); } - if (old_rep) { - // Likewise, we need to invoke destructors on the old array. If Element has - // no destructor, this loop will disappear. - e = &old_rep->elements[0]; - limit = &old_rep->elements[old_total_size]; - for (; e < limit; e++) { - e->Element::~Element(); - } - if (arena == NULL) { - delete[] reinterpret_cast(old_rep); - } - } + + // Likewise, we need to invoke destructors on the old array. + InternalDeallocate(old_rep, old_total_size); + } template @@ -2380,6 +2393,37 @@ template class AllocatedRepeatedPtrFieldBackInsertIterator private: RepeatedPtrField* field_; }; + +// Almost identical to AllocatedRepeatedPtrFieldBackInsertIterator. This one +// uses the UnsafeArenaAddAllocated instead. +template +class UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator + : public std::iterator { + public: + explicit UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator( + ::google::protobuf::RepeatedPtrField* const mutable_field) + : field_(mutable_field) { + } + UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator& operator=( + T const* const ptr_to_value) { + field_->UnsafeArenaAddAllocated(const_cast(ptr_to_value)); + return *this; + } + UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator& operator*() { + return *this; + } + UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator& operator++() { + return *this; + } + UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator& operator++( + int /* unused */) { + return *this; + } + + private: + ::google::protobuf::RepeatedPtrField* field_; +}; + } // namespace internal // Provides a back insert iterator for RepeatedField instances, @@ -2414,6 +2458,25 @@ AllocatedRepeatedPtrFieldBackInserter( mutable_field); } +// Similar to AllocatedRepeatedPtrFieldBackInserter, using +// UnsafeArenaAddAllocated instead of AddAllocated. +// This is slightly faster if that matters. It is also useful in legacy code +// that uses temporary ownership to avoid copies. Example: +// RepeatedPtrField temp_field; +// temp_field.AddAllocated(new T); +// ... // Do something with temp_field +// temp_field.ExtractSubrange(0, temp_field.size(), NULL); +// If you put temp_field on the arena this fails, because the ownership +// transfers to the arena at the "AddAllocated" call and is not released anymore +// causing a double delete. Using UnsafeArenaAddAllocated prevents this. +template +internal::UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator +UnsafeArenaAllocatedRepeatedPtrFieldBackInserter( + ::google::protobuf::RepeatedPtrField* const mutable_field) { + return internal::UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator( + mutable_field); +} + } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc index b45664b0b..39b24b337 100644 --- a/src/google/protobuf/repeated_field_unittest.cc +++ b/src/google/protobuf/repeated_field_unittest.cc @@ -1522,6 +1522,44 @@ TEST_F(RepeatedFieldInsertionIteratorsTest, EXPECT_EQ(testproto.DebugString(), goldenproto.DebugString()); } +TEST_F(RepeatedFieldInsertionIteratorsTest, + UnsafeArenaAllocatedRepeatedPtrFieldWithStringIntData) { + vector data; + TestAllTypes goldenproto; + for (int i = 0; i < 10; ++i) { + Nested* new_data = new Nested; + new_data->set_bb(i); + data.push_back(new_data); + + new_data = goldenproto.add_repeated_nested_message(); + new_data->set_bb(i); + } + TestAllTypes testproto; + std::copy(data.begin(), data.end(), + UnsafeArenaAllocatedRepeatedPtrFieldBackInserter( + testproto.mutable_repeated_nested_message())); + EXPECT_EQ(testproto.DebugString(), goldenproto.DebugString()); +} + +TEST_F(RepeatedFieldInsertionIteratorsTest, + UnsafeArenaAllocatedRepeatedPtrFieldWithString) { + vector data; + TestAllTypes goldenproto; + for (int i = 0; i < 10; ++i) { + string* new_data = new string; + *new_data = "name-" + SimpleItoa(i); + data.push_back(new_data); + + new_data = goldenproto.add_repeated_string(); + *new_data = "name-" + SimpleItoa(i); + } + TestAllTypes testproto; + std::copy(data.begin(), data.end(), + UnsafeArenaAllocatedRepeatedPtrFieldBackInserter( + testproto.mutable_repeated_string())); + EXPECT_EQ(testproto.DebugString(), goldenproto.DebugString()); +} + } // namespace } // namespace protobuf diff --git a/src/google/protobuf/source_context.pb.cc b/src/google/protobuf/source_context.pb.cc index f2eb7ae74..c67cd102f 100644 --- a/src/google/protobuf/source_context.pb.cc +++ b/src/google/protobuf/source_context.pb.cc @@ -177,12 +177,13 @@ SourceContext* SourceContext::New(::google::protobuf::Arena* arena) const { } void SourceContext::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.SourceContext) file_name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } bool SourceContext::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.SourceContext) for (;;) { @@ -262,6 +263,7 @@ void SourceContext::SerializeWithCachedSizes( } int SourceContext::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.SourceContext) int total_size = 0; // optional string file_name = 1; @@ -278,18 +280,22 @@ int SourceContext::ByteSize() const { } void SourceContext::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.SourceContext) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const SourceContext* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.SourceContext) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.SourceContext) MergeFrom(*source); } } void SourceContext::MergeFrom(const SourceContext& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.SourceContext) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.file_name().size() > 0) { @@ -298,12 +304,14 @@ void SourceContext::MergeFrom(const SourceContext& from) { } void SourceContext::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.SourceContext) if (&from == this) return; Clear(); MergeFrom(from); } void SourceContext::CopyFrom(const SourceContext& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.SourceContext) if (&from == this) return; Clear(); MergeFrom(from); @@ -365,6 +373,7 @@ void SourceContext::clear_file_name() { return file_name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* SourceContext::release_file_name() { + // @@protoc_insertion_point(field_release:google.protobuf.SourceContext.file_name) return file_name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } diff --git a/src/google/protobuf/source_context.pb.h b/src/google/protobuf/source_context.pb.h index 02e114607..ccfd365be 100644 --- a/src/google/protobuf/source_context.pb.h +++ b/src/google/protobuf/source_context.pb.h @@ -160,6 +160,7 @@ inline ::std::string* SourceContext::mutable_file_name() { return file_name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* SourceContext::release_file_name() { + // @@protoc_insertion_point(field_release:google.protobuf.SourceContext.file_name) return file_name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } diff --git a/src/google/protobuf/struct.pb.cc b/src/google/protobuf/struct.pb.cc index e020597a3..11ccabbf3 100644 --- a/src/google/protobuf/struct.pb.cc +++ b/src/google/protobuf/struct.pb.cc @@ -166,9 +166,11 @@ void protobuf_AddDesc_google_2fprotobuf_2fstruct_2eproto() { "\000\0220\n\nlist_value\030\006 \001(\0132\032.google.protobuf." "ListValueH\000B\006\n\004kind\"3\n\tListValue\022&\n\006valu" "es\030\001 \003(\0132\026.google.protobuf.Value*\033\n\tNull" - "Value\022\016\n\nNULL_VALUE\020\000BN\n\023com.google.prot" - "obufB\013StructProtoP\001\240\001\001\242\002\003GPB\252\002\036Google.Pr" - "otobuf.WellKnownTypesb\006proto3", 589); + "Value\022\016\n\nNULL_VALUE\020\000B\201\001\n\023com.google.pro" + "tobufB\013StructProtoP\001Z1github.com/golang/" + "protobuf/ptypes/struct;structpb\240\001\001\242\002\003GPB" + "\252\002\036Google.Protobuf.WellKnownTypesb\006proto" + "3", 641); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/struct.proto", &protobuf_RegisterTypes); Struct::default_instance_ = new Struct(); @@ -280,12 +282,13 @@ Struct* Struct::New(::google::protobuf::Arena* arena) const { } void Struct::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Struct) fields_.Clear(); } bool Struct::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Struct) for (;;) { @@ -383,6 +386,7 @@ void Struct::SerializeWithCachedSizes( } int Struct::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Struct) int total_size = 0; // map fields = 1; @@ -405,29 +409,35 @@ int Struct::ByteSize() const { } void Struct::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Struct) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Struct* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Struct) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Struct) MergeFrom(*source); } } void Struct::MergeFrom(const Struct& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Struct) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); fields_.MergeFrom(from.fields_); } void Struct::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Struct) if (&from == this) return; Clear(); MergeFrom(from); } void Struct::CopyFrom(const Struct& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Struct) if (&from == this) return; Clear(); MergeFrom(from); @@ -560,6 +570,7 @@ Value* Value::New(::google::protobuf::Arena* arena) const { } void Value::clear_kind() { +// @@protoc_insertion_point(one_of_clear_start:google.protobuf.Value) switch(kind_case()) { case kNullValue: { // No need to clear @@ -594,12 +605,13 @@ void Value::clear_kind() { void Value::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Value) clear_kind(); } bool Value::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Value) for (;;) { @@ -811,6 +823,7 @@ void Value::SerializeWithCachedSizes( } int Value::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Value) int total_size = 0; switch (kind_case()) { @@ -862,18 +875,22 @@ int Value::ByteSize() const { } void Value::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Value) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Value* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Value) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Value) MergeFrom(*source); } } void Value::MergeFrom(const Value& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Value) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); switch (from.kind_case()) { case kNullValue: { @@ -907,12 +924,14 @@ void Value::MergeFrom(const Value& from) { } void Value::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Value) if (&from == this) return; Clear(); MergeFrom(from); } void Value::CopyFrom(const Value& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Value) if (&from == this) return; Clear(); MergeFrom(from); @@ -1063,6 +1082,7 @@ void Value::clear_string_value() { return kind_.string_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Value::release_string_value() { + // @@protoc_insertion_point(field_release:google.protobuf.Value.string_value) if (has_string_value()) { clear_has_kind(); return kind_.string_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); @@ -1141,6 +1161,7 @@ void Value::clear_struct_value() { return kind_.struct_value_; } ::google::protobuf::Struct* Value::release_struct_value() { + // @@protoc_insertion_point(field_release:google.protobuf.Value.struct_value) if (has_struct_value()) { clear_has_kind(); ::google::protobuf::Struct* temp = kind_.struct_value_; @@ -1188,6 +1209,7 @@ void Value::clear_list_value() { return kind_.list_value_; } ::google::protobuf::ListValue* Value::release_list_value() { + // @@protoc_insertion_point(field_release:google.protobuf.Value.list_value) if (has_list_value()) { clear_has_kind(); ::google::protobuf::ListValue* temp = kind_.list_value_; @@ -1282,12 +1304,13 @@ ListValue* ListValue::New(::google::protobuf::Arena* arena) const { } void ListValue::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.ListValue) values_.Clear(); } bool ListValue::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.ListValue) for (;;) { @@ -1359,6 +1382,7 @@ void ListValue::SerializeWithCachedSizes( } int ListValue::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.ListValue) int total_size = 0; // repeated .google.protobuf.Value values = 1; @@ -1376,29 +1400,35 @@ int ListValue::ByteSize() const { } void ListValue::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.ListValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const ListValue* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.ListValue) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.ListValue) MergeFrom(*source); } } void ListValue::MergeFrom(const ListValue& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.ListValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); values_.MergeFrom(from.values_); } void ListValue::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.ListValue) if (&from == this) return; Clear(); MergeFrom(from); } void ListValue::CopyFrom(const ListValue& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.ListValue) if (&from == this) return; Clear(); MergeFrom(from); diff --git a/src/google/protobuf/struct.pb.h b/src/google/protobuf/struct.pb.h index 0527c8124..6a4764a7f 100644 --- a/src/google/protobuf/struct.pb.h +++ b/src/google/protobuf/struct.pb.h @@ -550,6 +550,7 @@ inline ::std::string* Value::mutable_string_value() { return kind_.string_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Value::release_string_value() { + // @@protoc_insertion_point(field_release:google.protobuf.Value.string_value) if (has_string_value()) { clear_has_kind(); return kind_.string_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); @@ -628,6 +629,7 @@ inline ::google::protobuf::Struct* Value::mutable_struct_value() { return kind_.struct_value_; } inline ::google::protobuf::Struct* Value::release_struct_value() { + // @@protoc_insertion_point(field_release:google.protobuf.Value.struct_value) if (has_struct_value()) { clear_has_kind(); ::google::protobuf::Struct* temp = kind_.struct_value_; @@ -675,6 +677,7 @@ inline ::google::protobuf::ListValue* Value::mutable_list_value() { return kind_.list_value_; } inline ::google::protobuf::ListValue* Value::release_list_value() { + // @@protoc_insertion_point(field_release:google.protobuf.Value.list_value) if (has_list_value()) { clear_has_kind(); ::google::protobuf::ListValue* temp = kind_.list_value_; diff --git a/src/google/protobuf/struct.proto b/src/google/protobuf/struct.proto index 8562e2c14..beeba8118 100644 --- a/src/google/protobuf/struct.proto +++ b/src/google/protobuf/struct.proto @@ -33,6 +33,7 @@ syntax = "proto3"; package google.protobuf; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/struct;structpb"; option java_package = "com.google.protobuf"; option java_outer_classname = "StructProto"; option java_multiple_files = true; @@ -49,7 +50,7 @@ option objc_class_prefix = "GPB"; // // The JSON representation for `Struct` is JSON object. message Struct { - // Map of dynamically typed values. + // Unordered map of dynamically typed values. map fields = 1; } diff --git a/src/google/protobuf/stubs/hash.h b/src/google/protobuf/stubs/hash.h index 583343222..bbd8ee650 100755 --- a/src/google/protobuf/stubs/hash.h +++ b/src/google/protobuf/stubs/hash.h @@ -214,6 +214,8 @@ class hash_map : public std::map { hash_map(int a = 0, const HashFcn& b = HashFcn(), const EqualKey& c = EqualKey(), const Alloc& d = Alloc()) : BaseClass(b, d) {} + + HashFcn hash_function() const { return HashFcn(); } }; template { public: hash_set(int = 0) {} + + HashFcn hash_function() const { return HashFcn(); } }; #elif defined(_MSC_VER) && !defined(_STLPORT_VERSION) @@ -257,6 +261,8 @@ class hash_map hash_map(int a = 0, const HashFcn& b = HashFcn(), const EqualKey& c = EqualKey(), const Alloc& d = Alloc()) : BaseClass(a, b, c, d) {} + + HashFcn hash_function() const { return HashFcn(); } }; template , @@ -266,6 +272,8 @@ class hash_set Key, HashFcn, EqualKey> { public: hash_set(int = 0) {} + + HashFcn hash_function() const { return HashFcn(); } }; #else @@ -315,6 +323,8 @@ class hash_map hash_map(int a = 0, const HashFcn& b = HashFcn(), const EqualKey& c = EqualKey(), const Alloc& d = Alloc()) : BaseClass(a, b, c, d) {} + + HashFcn hash_function() const { return HashFcn(); } }; template , @@ -324,6 +334,8 @@ class hash_set Key, HashFcn, EqualKey> { public: hash_set(int = 0) {} + + HashFcn hash_function() const { return HashFcn(); } }; #endif // !GOOGLE_PROTOBUF_MISSING_HASH diff --git a/src/google/protobuf/stubs/int128.cc b/src/google/protobuf/stubs/int128.cc index d80c64f23..3a36b4b10 100644 --- a/src/google/protobuf/stubs/int128.cc +++ b/src/google/protobuf/stubs/int128.cc @@ -31,7 +31,7 @@ #include #include -#include // NOLINT(readability/streams) +#include // NOLINT(readability/streams) #include namespace google { diff --git a/src/google/protobuf/stubs/logging.h b/src/google/protobuf/stubs/logging.h index 3108db8c8..f69605d9f 100644 --- a/src/google/protobuf/stubs/logging.h +++ b/src/google/protobuf/stubs/logging.h @@ -174,7 +174,7 @@ T* CheckNotNull(const char* /* file */, int /* line */, #ifdef NDEBUG -#define GOOGLE_DLOG GOOGLE_LOG_IF(INFO, false) +#define GOOGLE_DLOG(LEVEL) GOOGLE_LOG_IF(LEVEL, false) #define GOOGLE_DCHECK(EXPRESSION) while(false) GOOGLE_CHECK(EXPRESSION) #define GOOGLE_DCHECK_OK(E) GOOGLE_DCHECK(::google::protobuf::internal::IsOk(E)) diff --git a/src/google/protobuf/stubs/mathutil.h b/src/google/protobuf/stubs/mathutil.h index 3a1ef8a8c..27956a8ea 100644 --- a/src/google/protobuf/stubs/mathutil.h +++ b/src/google/protobuf/stubs/mathutil.h @@ -83,7 +83,7 @@ class MathUtil { if (value == T(0) || ::google::protobuf::internal::IsNan(value)) { return value; } - return value > T(0) ? value : -value; + return value > T(0) ? 1 : -1; } template diff --git a/src/google/protobuf/stubs/statusor.h b/src/google/protobuf/stubs/statusor.h index ad848701b..29f869ad5 100644 --- a/src/google/protobuf/stubs/statusor.h +++ b/src/google/protobuf/stubs/statusor.h @@ -224,7 +224,7 @@ inline StatusOr& StatusOr::operator=(const StatusOr& other) { template template inline StatusOr::StatusOr(const StatusOr& other) - : status_(other.status_), value_(other.status_.ok() ? other.value_ : NULL) { + : status_(other.status_), value_(other.status_.ok() ? other.value_ : T()) { } template diff --git a/src/google/protobuf/stubs/stringpiece.h b/src/google/protobuf/stubs/stringpiece.h index ec3ffd5b0..916716594 100644 --- a/src/google/protobuf/stubs/stringpiece.h +++ b/src/google/protobuf/stubs/stringpiece.h @@ -435,6 +435,44 @@ inline bool operator>=(StringPiece x, StringPiece y) { // allow StringPiece to be logged extern std::ostream& operator<<(std::ostream& o, StringPiece piece); +namespace internal { +// StringPiece is not a POD and can not be used in an union (pre C++11). We +// need a POD version of it. +struct StringPiecePod { + // Create from a StringPiece. + static StringPiecePod CreateFromStringPiece(StringPiece str) { + StringPiecePod pod; + pod.data_ = str.data(); + pod.size_ = str.size(); + return pod; + } + + // Cast to StringPiece. + operator StringPiece() const { return StringPiece(data_, size_); } + + bool operator==(const char* value) const { + return StringPiece(data_, size_) == StringPiece(value); + } + + char operator[](stringpiece_ssize_type i) const { + assert(0 <= i); + assert(i < size_); + return data_[i]; + } + + const char* data() const { return data_; } + + stringpiece_ssize_type size() const { + return size_; + } + + std::string ToString() const { return std::string(data_, size_); } + private: + const char* data_; + stringpiece_ssize_type size_; +}; + +} // namespace internal } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/stubs/stringprintf.cc b/src/google/protobuf/stubs/stringprintf.cc index 3272d8e38..83fdfe454 100644 --- a/src/google/protobuf/stubs/stringprintf.cc +++ b/src/google/protobuf/stubs/stringprintf.cc @@ -37,7 +37,6 @@ #include // MSVC requires this for _vsnprintf #include #include -#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/stubs/strutil.h b/src/google/protobuf/stubs/strutil.h index 27d475754..8bdd61103 100644 --- a/src/google/protobuf/stubs/strutil.h +++ b/src/google/protobuf/stubs/strutil.h @@ -648,6 +648,9 @@ struct LIBPROTOBUF_EXPORT AlphaNum { AlphaNum(StringPiece str) : piece_data_(str.data()), piece_size_(str.size()) {} + AlphaNum(internal::StringPiecePod str) + : piece_data_(str.data()), piece_size_(str.size()) {} + size_t size() const { return piece_size_; } const char *data() const { return piece_data_; } @@ -847,6 +850,11 @@ LIBPROTOBUF_EXPORT void Base64Escape(const unsigned char* src, int szsrc, LIBPROTOBUF_EXPORT void WebSafeBase64Escape(const unsigned char* src, int szsrc, string* dest, bool do_padding); +inline bool IsValidCodePoint(uint32 code_point) { + return code_point < 0xD800 || + (code_point >= 0xE000 && code_point <= 0x10FFFF); +} + static const int UTFmax = 4; // ---------------------------------------------------------------------- // EncodeAsUTF8Char() diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc index b0a5ce63c..c0dfd53fd 100644 --- a/src/google/protobuf/text_format.cc +++ b/src/google/protobuf/text_format.cc @@ -91,7 +91,10 @@ inline bool GetAnyFieldDescriptors(const Message& message, string Message::DebugString() const { string debug_string; - TextFormat::PrintToString(*this, &debug_string); + TextFormat::Printer printer; + printer.SetExpandAny(true); + + printer.PrintToString(*this, &debug_string); return debug_string; } @@ -101,6 +104,7 @@ string Message::ShortDebugString() const { TextFormat::Printer printer; printer.SetSingleLineMode(true); + printer.SetExpandAny(true); printer.PrintToString(*this, &debug_string); // Single line mode currently might have an extra space at the end. @@ -117,6 +121,7 @@ string Message::Utf8DebugString() const { TextFormat::Printer printer; printer.SetUseUtf8StringEscaping(true); + printer.SetExpandAny(true); printer.PrintToString(*this, &debug_string); @@ -1153,10 +1158,10 @@ class TextFormat::Printer::TextGenerator { } // Print text to the output stream. - void Print(const char* text, int size) { - int pos = 0; // The number of bytes we've written so far. + void Print(const char* text, size_t size) { + size_t pos = 0; // The number of bytes we've written so far. - for (int i = 0; i < size; i++) { + for (size_t i = 0; i < size; i++) { if (text[i] == '\n') { // Saw newline. If there is more text, we may need to insert an indent // here. So, write what we have so far, including the '\n'. @@ -1181,7 +1186,7 @@ class TextFormat::Printer::TextGenerator { private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TextGenerator); - void Write(const char* data, int size) { + void Write(const char* data, size_t size) { if (failed_) return; if (size == 0) return; @@ -1780,14 +1785,12 @@ void TextFormat::Printer::PrintFieldValue( ? reflection->GetRepeatedStringReference( message, field, index, &scratch) : reflection->GetStringReference(message, field, &scratch); - int64 size = value.size(); - if (truncate_string_field_longer_than_ > 0) { - size = std::min(truncate_string_field_longer_than_, - static_cast(value.size())); - } - string truncated_value(value.substr(0, size) + "......"); const string* value_to_print = &value; - if (size < value.size()) { + string truncated_value; + if (truncate_string_field_longer_than_ > 0 && + truncate_string_field_longer_than_ < value.size()) { + truncated_value = value.substr(0, truncate_string_field_longer_than_) + + "......"; value_to_print = &truncated_value; } if (field->type() == FieldDescriptor::TYPE_STRING) { diff --git a/src/google/protobuf/text_format_unittest.cc b/src/google/protobuf/text_format_unittest.cc index 8d61be192..58aa3f8d6 100644 --- a/src/google/protobuf/text_format_unittest.cc +++ b/src/google/protobuf/text_format_unittest.cc @@ -44,6 +44,8 @@ #include #include +#include +#include #include #include #include @@ -52,10 +54,10 @@ #include #include #include -#include #include #include #include +#include namespace google { diff --git a/src/google/protobuf/timestamp.pb.cc b/src/google/protobuf/timestamp.pb.cc index c1c4402c3..7cdf5093d 100644 --- a/src/google/protobuf/timestamp.pb.cc +++ b/src/google/protobuf/timestamp.pb.cc @@ -83,9 +83,10 @@ void protobuf_AddDesc_google_2fprotobuf_2ftimestamp_2eproto() { ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( "\n\037google/protobuf/timestamp.proto\022\017googl" "e.protobuf\"+\n\tTimestamp\022\017\n\007seconds\030\001 \001(\003" - "\022\r\n\005nanos\030\002 \001(\005BT\n\023com.google.protobufB\016" - "TimestampProtoP\001\240\001\001\370\001\001\242\002\003GPB\252\002\036Google.Pr" - "otobuf.WellKnownTypesb\006proto3", 189); + "\022\r\n\005nanos\030\002 \001(\005B\201\001\n\023com.google.protobufB" + "\016TimestampProtoP\001Z+github.com/golang/pro" + "tobuf/ptypes/timestamp\240\001\001\370\001\001\242\002\003GPB\252\002\036Goo" + "gle.Protobuf.WellKnownTypesb\006proto3", 235); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/timestamp.proto", &protobuf_RegisterTypes); Timestamp::default_instance_ = new Timestamp(); @@ -192,8 +193,17 @@ Timestamp* Timestamp::New(::google::protobuf::Arena* arena) const { } void Timestamp::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Timestamp) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(Timestamp, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -209,7 +219,7 @@ void Timestamp::Clear() { bool Timestamp::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Timestamp) for (;;) { @@ -301,6 +311,7 @@ void Timestamp::SerializeWithCachedSizes( } int Timestamp::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Timestamp) int total_size = 0; // optional int64 seconds = 1; @@ -324,18 +335,22 @@ int Timestamp::ByteSize() const { } void Timestamp::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Timestamp) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Timestamp* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Timestamp) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Timestamp) MergeFrom(*source); } } void Timestamp::MergeFrom(const Timestamp& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Timestamp) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.seconds() != 0) { set_seconds(from.seconds()); @@ -346,12 +361,14 @@ void Timestamp::MergeFrom(const Timestamp& from) { } void Timestamp::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Timestamp) if (&from == this) return; Clear(); MergeFrom(from); } void Timestamp::CopyFrom(const Timestamp& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Timestamp) if (&from == this) return; Clear(); MergeFrom(from); diff --git a/src/google/protobuf/timestamp.proto b/src/google/protobuf/timestamp.proto index b51fc3fa4..7992a8588 100644 --- a/src/google/protobuf/timestamp.proto +++ b/src/google/protobuf/timestamp.proto @@ -34,6 +34,7 @@ package google.protobuf; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; option cc_enable_arenas = true; +option go_package = "github.com/golang/protobuf/ptypes/timestamp"; option java_package = "com.google.protobuf"; option java_outer_classname = "TimestampProto"; option java_multiple_files = true; diff --git a/src/google/protobuf/type.pb.cc b/src/google/protobuf/type.pb.cc index 7b47b3bdd..759cab278 100644 --- a/src/google/protobuf/type.pb.cc +++ b/src/google/protobuf/type.pb.cc @@ -359,6 +359,7 @@ Type* Type::New(::google::protobuf::Arena* arena) const { } void Type::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Type) name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; @@ -370,7 +371,7 @@ void Type::Clear() { bool Type::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Type) for (;;) { @@ -603,6 +604,7 @@ void Type::SerializeWithCachedSizes( } int Type::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Type) int total_size = 0; // optional string name = 1; @@ -655,18 +657,22 @@ int Type::ByteSize() const { } void Type::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Type) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Type* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Type) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Type) MergeFrom(*source); } } void Type::MergeFrom(const Type& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Type) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); fields_.MergeFrom(from.fields_); oneofs_.MergeFrom(from.oneofs_); @@ -684,12 +690,14 @@ void Type::MergeFrom(const Type& from) { } void Type::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Type) if (&from == this) return; Clear(); MergeFrom(from); } void Type::CopyFrom(const Type& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Type) if (&from == this) return; Clear(); MergeFrom(from); @@ -756,6 +764,7 @@ void Type::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Type::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Type.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -828,6 +837,7 @@ void Type::clear_oneofs() { // @@protoc_insertion_point(field_set_pointer:google.protobuf.Type.oneofs) } ::std::string* Type::add_oneofs() { + // @@protoc_insertion_point(field_add_mutable:google.protobuf.Type.oneofs) return oneofs_.Add(); } void Type::add_oneofs(const ::std::string& value) { @@ -904,6 +914,7 @@ const ::google::protobuf::SourceContext& Type::source_context() const { return source_context_; } ::google::protobuf::SourceContext* Type::release_source_context() { + // @@protoc_insertion_point(field_release:google.protobuf.Type.source_context) ::google::protobuf::SourceContext* temp = source_context_; source_context_ = NULL; @@ -1104,8 +1115,17 @@ Field* Field::New(::google::protobuf::Arena* arena) const { } void Field::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Field) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(Field, f) \ + _Pragma("clang diagnostic pop") +#else #define ZR_HELPER_(f) reinterpret_cast(\ &reinterpret_cast(16)->f) +#endif #define ZR_(first, last) do {\ ::memset(&first, 0,\ @@ -1128,7 +1148,7 @@ void Field::Clear() { bool Field::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Field) for (;;) { @@ -1483,6 +1503,7 @@ void Field::SerializeWithCachedSizes( } int Field::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Field) int total_size = 0; // optional .google.protobuf.Field.Kind kind = 1; @@ -1559,18 +1580,22 @@ int Field::ByteSize() const { } void Field::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Field) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Field* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Field) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Field) MergeFrom(*source); } } void Field::MergeFrom(const Field& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Field) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); options_.MergeFrom(from.options_); if (from.kind() != 0) { @@ -1607,12 +1632,14 @@ void Field::MergeFrom(const Field& from) { } void Field::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Field) if (&from == this) return; Clear(); MergeFrom(from); } void Field::CopyFrom(const Field& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Field) if (&from == this) return; Clear(); MergeFrom(from); @@ -1725,6 +1752,7 @@ void Field::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Field::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Field.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1768,6 +1796,7 @@ void Field::clear_type_url() { return type_url_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Field::release_type_url() { + // @@protoc_insertion_point(field_release:google.protobuf.Field.type_url) return type_url_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1869,6 +1898,7 @@ void Field::clear_json_name() { return json_name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Field::release_json_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Field.json_name) return json_name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1912,6 +1942,7 @@ void Field::clear_default_value() { return default_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Field::release_default_value() { + // @@protoc_insertion_point(field_release:google.protobuf.Field.default_value) return default_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -2003,6 +2034,7 @@ Enum* Enum::New(::google::protobuf::Arena* arena) const { } void Enum::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Enum) name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; @@ -2013,7 +2045,7 @@ void Enum::Clear() { bool Enum::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Enum) for (;;) { @@ -2206,6 +2238,7 @@ void Enum::SerializeWithCachedSizes( } int Enum::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Enum) int total_size = 0; // optional string name = 1; @@ -2251,18 +2284,22 @@ int Enum::ByteSize() const { } void Enum::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Enum) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Enum* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Enum) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Enum) MergeFrom(*source); } } void Enum::MergeFrom(const Enum& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Enum) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); enumvalue_.MergeFrom(from.enumvalue_); options_.MergeFrom(from.options_); @@ -2279,12 +2316,14 @@ void Enum::MergeFrom(const Enum& from) { } void Enum::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Enum) if (&from == this) return; Clear(); MergeFrom(from); } void Enum::CopyFrom(const Enum& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Enum) if (&from == this) return; Clear(); MergeFrom(from); @@ -2350,6 +2389,7 @@ void Enum::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Enum::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Enum.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -2444,6 +2484,7 @@ const ::google::protobuf::SourceContext& Enum::source_context() const { return source_context_; } ::google::protobuf::SourceContext* Enum::release_source_context() { + // @@protoc_insertion_point(field_release:google.protobuf.Enum.source_context) ::google::protobuf::SourceContext* temp = source_context_; source_context_ = NULL; @@ -2547,6 +2588,7 @@ EnumValue* EnumValue::New(::google::protobuf::Arena* arena) const { } void EnumValue::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.EnumValue) name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); number_ = 0; options_.Clear(); @@ -2554,7 +2596,7 @@ void EnumValue::Clear() { bool EnumValue::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.EnumValue) for (;;) { @@ -2689,6 +2731,7 @@ void EnumValue::SerializeWithCachedSizes( } int EnumValue::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.EnumValue) int total_size = 0; // optional string name = 1; @@ -2720,18 +2763,22 @@ int EnumValue::ByteSize() const { } void EnumValue::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.EnumValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const EnumValue* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.EnumValue) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.EnumValue) MergeFrom(*source); } } void EnumValue::MergeFrom(const EnumValue& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.EnumValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); options_.MergeFrom(from.options_); if (from.name().size() > 0) { @@ -2744,12 +2791,14 @@ void EnumValue::MergeFrom(const EnumValue& from) { } void EnumValue::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.EnumValue) if (&from == this) return; Clear(); MergeFrom(from); } void EnumValue::CopyFrom(const EnumValue& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.EnumValue) if (&from == this) return; Clear(); MergeFrom(from); @@ -2813,6 +2862,7 @@ void EnumValue::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* EnumValue::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.EnumValue.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -2944,6 +2994,7 @@ Option* Option::New(::google::protobuf::Arena* arena) const { } void Option::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Option) name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); if (GetArenaNoVirtual() == NULL && value_ != NULL) delete value_; value_ = NULL; @@ -2951,7 +3002,7 @@ void Option::Clear() { bool Option::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Option) for (;;) { @@ -3057,6 +3108,7 @@ void Option::SerializeWithCachedSizes( } int Option::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Option) int total_size = 0; // optional string name = 1; @@ -3080,18 +3132,22 @@ int Option::ByteSize() const { } void Option::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Option) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Option* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Option) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Option) MergeFrom(*source); } } void Option::MergeFrom(const Option& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Option) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.name().size() > 0) { @@ -3103,12 +3159,14 @@ void Option::MergeFrom(const Option& from) { } void Option::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Option) if (&from == this) return; Clear(); MergeFrom(from); } void Option::CopyFrom(const Option& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Option) if (&from == this) return; Clear(); MergeFrom(from); @@ -3171,6 +3229,7 @@ void Option::clear_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } ::std::string* Option::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Option.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -3205,6 +3264,7 @@ const ::google::protobuf::Any& Option::value() const { return value_; } ::google::protobuf::Any* Option::release_value() { + // @@protoc_insertion_point(field_release:google.protobuf.Option.value) ::google::protobuf::Any* temp = value_; value_ = NULL; diff --git a/src/google/protobuf/type.pb.h b/src/google/protobuf/type.pb.h index 76fe8a65c..4255fa8c2 100644 --- a/src/google/protobuf/type.pb.h +++ b/src/google/protobuf/type.pb.h @@ -328,25 +328,44 @@ class LIBPROTOBUF_EXPORT Field : public ::google::protobuf::Message { // nested types ---------------------------------------------------- typedef Field_Kind Kind; - static const Kind TYPE_UNKNOWN = Field_Kind_TYPE_UNKNOWN; - static const Kind TYPE_DOUBLE = Field_Kind_TYPE_DOUBLE; - static const Kind TYPE_FLOAT = Field_Kind_TYPE_FLOAT; - static const Kind TYPE_INT64 = Field_Kind_TYPE_INT64; - static const Kind TYPE_UINT64 = Field_Kind_TYPE_UINT64; - static const Kind TYPE_INT32 = Field_Kind_TYPE_INT32; - static const Kind TYPE_FIXED64 = Field_Kind_TYPE_FIXED64; - static const Kind TYPE_FIXED32 = Field_Kind_TYPE_FIXED32; - static const Kind TYPE_BOOL = Field_Kind_TYPE_BOOL; - static const Kind TYPE_STRING = Field_Kind_TYPE_STRING; - static const Kind TYPE_GROUP = Field_Kind_TYPE_GROUP; - static const Kind TYPE_MESSAGE = Field_Kind_TYPE_MESSAGE; - static const Kind TYPE_BYTES = Field_Kind_TYPE_BYTES; - static const Kind TYPE_UINT32 = Field_Kind_TYPE_UINT32; - static const Kind TYPE_ENUM = Field_Kind_TYPE_ENUM; - static const Kind TYPE_SFIXED32 = Field_Kind_TYPE_SFIXED32; - static const Kind TYPE_SFIXED64 = Field_Kind_TYPE_SFIXED64; - static const Kind TYPE_SINT32 = Field_Kind_TYPE_SINT32; - static const Kind TYPE_SINT64 = Field_Kind_TYPE_SINT64; + static const Kind TYPE_UNKNOWN = + Field_Kind_TYPE_UNKNOWN; + static const Kind TYPE_DOUBLE = + Field_Kind_TYPE_DOUBLE; + static const Kind TYPE_FLOAT = + Field_Kind_TYPE_FLOAT; + static const Kind TYPE_INT64 = + Field_Kind_TYPE_INT64; + static const Kind TYPE_UINT64 = + Field_Kind_TYPE_UINT64; + static const Kind TYPE_INT32 = + Field_Kind_TYPE_INT32; + static const Kind TYPE_FIXED64 = + Field_Kind_TYPE_FIXED64; + static const Kind TYPE_FIXED32 = + Field_Kind_TYPE_FIXED32; + static const Kind TYPE_BOOL = + Field_Kind_TYPE_BOOL; + static const Kind TYPE_STRING = + Field_Kind_TYPE_STRING; + static const Kind TYPE_GROUP = + Field_Kind_TYPE_GROUP; + static const Kind TYPE_MESSAGE = + Field_Kind_TYPE_MESSAGE; + static const Kind TYPE_BYTES = + Field_Kind_TYPE_BYTES; + static const Kind TYPE_UINT32 = + Field_Kind_TYPE_UINT32; + static const Kind TYPE_ENUM = + Field_Kind_TYPE_ENUM; + static const Kind TYPE_SFIXED32 = + Field_Kind_TYPE_SFIXED32; + static const Kind TYPE_SFIXED64 = + Field_Kind_TYPE_SFIXED64; + static const Kind TYPE_SINT32 = + Field_Kind_TYPE_SINT32; + static const Kind TYPE_SINT64 = + Field_Kind_TYPE_SINT64; static inline bool Kind_IsValid(int value) { return Field_Kind_IsValid(value); } @@ -369,10 +388,14 @@ class LIBPROTOBUF_EXPORT Field : public ::google::protobuf::Message { } typedef Field_Cardinality Cardinality; - static const Cardinality CARDINALITY_UNKNOWN = Field_Cardinality_CARDINALITY_UNKNOWN; - static const Cardinality CARDINALITY_OPTIONAL = Field_Cardinality_CARDINALITY_OPTIONAL; - static const Cardinality CARDINALITY_REQUIRED = Field_Cardinality_CARDINALITY_REQUIRED; - static const Cardinality CARDINALITY_REPEATED = Field_Cardinality_CARDINALITY_REPEATED; + static const Cardinality CARDINALITY_UNKNOWN = + Field_Cardinality_CARDINALITY_UNKNOWN; + static const Cardinality CARDINALITY_OPTIONAL = + Field_Cardinality_CARDINALITY_OPTIONAL; + static const Cardinality CARDINALITY_REQUIRED = + Field_Cardinality_CARDINALITY_REQUIRED; + static const Cardinality CARDINALITY_REPEATED = + Field_Cardinality_CARDINALITY_REPEATED; static inline bool Cardinality_IsValid(int value) { return Field_Cardinality_IsValid(value); } @@ -865,6 +888,7 @@ inline ::std::string* Type::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Type::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Type.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -937,6 +961,7 @@ inline void Type::set_oneofs(int index, const char* value, size_t size) { // @@protoc_insertion_point(field_set_pointer:google.protobuf.Type.oneofs) } inline ::std::string* Type::add_oneofs() { + // @@protoc_insertion_point(field_add_mutable:google.protobuf.Type.oneofs) return oneofs_.Add(); } inline void Type::add_oneofs(const ::std::string& value) { @@ -1013,6 +1038,7 @@ inline ::google::protobuf::SourceContext* Type::mutable_source_context() { return source_context_; } inline ::google::protobuf::SourceContext* Type::release_source_context() { + // @@protoc_insertion_point(field_release:google.protobuf.Type.source_context) ::google::protobuf::SourceContext* temp = source_context_; source_context_ = NULL; @@ -1119,6 +1145,7 @@ inline ::std::string* Field::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Field::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Field.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1162,6 +1189,7 @@ inline ::std::string* Field::mutable_type_url() { return type_url_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Field::release_type_url() { + // @@protoc_insertion_point(field_release:google.protobuf.Field.type_url) return type_url_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1263,6 +1291,7 @@ inline ::std::string* Field::mutable_json_name() { return json_name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Field::release_json_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Field.json_name) return json_name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1306,6 +1335,7 @@ inline ::std::string* Field::mutable_default_value() { return default_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Field::release_default_value() { + // @@protoc_insertion_point(field_release:google.protobuf.Field.default_value) return default_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1353,6 +1383,7 @@ inline ::std::string* Enum::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Enum::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Enum.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1447,6 +1478,7 @@ inline ::google::protobuf::SourceContext* Enum::mutable_source_context() { return source_context_; } inline ::google::protobuf::SourceContext* Enum::release_source_context() { + // @@protoc_insertion_point(field_release:google.protobuf.Enum.source_context) ::google::protobuf::SourceContext* temp = source_context_; source_context_ = NULL; @@ -1511,6 +1543,7 @@ inline ::std::string* EnumValue::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* EnumValue::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.EnumValue.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1602,6 +1635,7 @@ inline ::std::string* Option::mutable_name() { return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline ::std::string* Option::release_name() { + // @@protoc_insertion_point(field_release:google.protobuf.Option.name) return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } @@ -1636,6 +1670,7 @@ inline ::google::protobuf::Any* Option::mutable_value() { return value_; } inline ::google::protobuf::Any* Option::release_value() { + // @@protoc_insertion_point(field_release:google.protobuf.Option.value) ::google::protobuf::Any* temp = value_; value_ = NULL; diff --git a/src/google/protobuf/unittest.proto b/src/google/protobuf/unittest.proto index 85fe61537..da56ae0a2 100644 --- a/src/google/protobuf/unittest.proto +++ b/src/google/protobuf/unittest.proto @@ -696,6 +696,7 @@ message TestRequiredOneof { } } + // Test messages for packed fields message TestPackedTypes { diff --git a/src/google/protobuf/unittest_lite.proto b/src/google/protobuf/unittest_lite.proto index 662c0e461..878ec7c1d 100644 --- a/src/google/protobuf/unittest_lite.proto +++ b/src/google/protobuf/unittest_lite.proto @@ -43,8 +43,10 @@ option java_package = "com.google.protobuf"; // Same as TestAllTypes but with the lite runtime. message TestAllTypesLite { + message NestedMessage { optional int32 bb = 1; + optional int64 cc = 2; } enum NestedEnum { @@ -159,6 +161,7 @@ message TestAllTypesLite { NestedMessage oneof_nested_message = 112; string oneof_string = 113; bytes oneof_bytes = 114; + NestedMessage oneof_lazy_nested_message = 115 [lazy = true]; } } @@ -383,3 +386,22 @@ message TestEmptyMessageLite{ message TestEmptyMessageWithExtensionsLite { extensions 1 to max; } + +enum V1EnumLite { + V1_FIRST = 1; +} + +enum V2EnumLite { + V2_FIRST = 1; + V2_SECOND = 2; +} + +message V1MessageLite { + required int32 int_field = 1; + optional V1EnumLite enum_field = 2 [ default = V1_FIRST ]; +} + +message V2MessageLite { + required int32 int_field = 1; + optional V2EnumLite enum_field = 2 [ default = V2_FIRST ]; +} diff --git a/src/google/protobuf/unittest_proto3_arena_lite.proto b/src/google/protobuf/unittest_proto3_arena_lite.proto new file mode 100644 index 000000000..5a60b90f5 --- /dev/null +++ b/src/google/protobuf/unittest_proto3_arena_lite.proto @@ -0,0 +1,207 @@ +// 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. + +syntax = "proto3"; + +option cc_enable_arenas = true; +option optimize_for = LITE_RUNTIME; + +import "google/protobuf/unittest_import.proto"; + +package proto3_arena_lite_unittest; + +// This proto includes every type of field in both singular and repeated +// forms. +message TestAllTypes { + message NestedMessage { + // The field name "b" fails to compile in proto1 because it conflicts with + // a local variable named "b" in one of the generated methods. Doh. + // This file needs to compile in proto1 to test backwards-compatibility. + int32 bb = 1; + } + + enum NestedEnum { + ZERO = 0; + FOO = 1; + BAR = 2; + BAZ = 3; + NEG = -1; // Intentionally negative. + } + + // Singular + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + sint32 optional_sint32 = 5; + sint64 optional_sint64 = 6; + fixed32 optional_fixed32 = 7; + fixed64 optional_fixed64 = 8; + sfixed32 optional_sfixed32 = 9; + sfixed64 optional_sfixed64 = 10; + float optional_float = 11; + double optional_double = 12; + bool optional_bool = 13; + string optional_string = 14; + bytes optional_bytes = 15; + + // Groups are not allowed in proto3. + // optional group OptionalGroup = 16 { + // optional int32 a = 17; + // } + + NestedMessage optional_nested_message = 18; + ForeignMessage optional_foreign_message = 19; + protobuf_unittest_import.ImportMessage optional_import_message = 20; + + NestedEnum optional_nested_enum = 21; + ForeignEnum optional_foreign_enum = 22; + + // Omitted (compared to unittest.proto) because proto2 enums are not allowed + // inside proto2 messages. + // + // optional protobuf_unittest_import.ImportEnum optional_import_enum = 23; + + string optional_string_piece = 24 [ctype=STRING_PIECE]; + string optional_cord = 25 [ctype=CORD]; + + // Defined in unittest_import_public.proto + protobuf_unittest_import.PublicImportMessage + optional_public_import_message = 26; + + NestedMessage optional_lazy_message = 27 [lazy=true]; + + // Repeated + repeated int32 repeated_int32 = 31; + repeated int64 repeated_int64 = 32; + repeated uint32 repeated_uint32 = 33; + repeated uint64 repeated_uint64 = 34; + repeated sint32 repeated_sint32 = 35; + repeated sint64 repeated_sint64 = 36; + repeated fixed32 repeated_fixed32 = 37; + repeated fixed64 repeated_fixed64 = 38; + repeated sfixed32 repeated_sfixed32 = 39; + repeated sfixed64 repeated_sfixed64 = 40; + repeated float repeated_float = 41; + repeated double repeated_double = 42; + repeated bool repeated_bool = 43; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + + // Groups are not allowed in proto3. + // repeated group RepeatedGroup = 46 { + // optional int32 a = 47; + // } + + repeated NestedMessage repeated_nested_message = 48; + repeated ForeignMessage repeated_foreign_message = 49; + repeated protobuf_unittest_import.ImportMessage repeated_import_message = 50; + + repeated NestedEnum repeated_nested_enum = 51; + repeated ForeignEnum repeated_foreign_enum = 52; + + // Omitted (compared to unittest.proto) because proto2 enums are not allowed + // inside proto2 messages. + // + // repeated protobuf_unittest_import.ImportEnum repeated_import_enum = 53; + + repeated string repeated_string_piece = 54 [ctype=STRING_PIECE]; + repeated string repeated_cord = 55 [ctype=CORD]; + + repeated NestedMessage repeated_lazy_message = 57 [lazy=true]; + + oneof oneof_field { + uint32 oneof_uint32 = 111; + NestedMessage oneof_nested_message = 112; + string oneof_string = 113; + bytes oneof_bytes = 114; + } +} + +// Test messages for packed fields + +message TestPackedTypes { + repeated int32 packed_int32 = 90 [packed = true]; + repeated int64 packed_int64 = 91 [packed = true]; + repeated uint32 packed_uint32 = 92 [packed = true]; + repeated uint64 packed_uint64 = 93 [packed = true]; + repeated sint32 packed_sint32 = 94 [packed = true]; + repeated sint64 packed_sint64 = 95 [packed = true]; + repeated fixed32 packed_fixed32 = 96 [packed = true]; + repeated fixed64 packed_fixed64 = 97 [packed = true]; + repeated sfixed32 packed_sfixed32 = 98 [packed = true]; + repeated sfixed64 packed_sfixed64 = 99 [packed = true]; + repeated float packed_float = 100 [packed = true]; + repeated double packed_double = 101 [packed = true]; + repeated bool packed_bool = 102 [packed = true]; + repeated ForeignEnum packed_enum = 103 [packed = true]; +} + +// Explicitly set packed to false +message TestUnpackedTypes { + repeated int32 repeated_int32 = 1 [packed = false]; + repeated int64 repeated_int64 = 2 [packed = false]; + repeated uint32 repeated_uint32 = 3 [packed = false]; + repeated uint64 repeated_uint64 = 4 [packed = false]; + repeated sint32 repeated_sint32 = 5 [packed = false]; + repeated sint64 repeated_sint64 = 6 [packed = false]; + repeated fixed32 repeated_fixed32 = 7 [packed = false]; + repeated fixed64 repeated_fixed64 = 8 [packed = false]; + repeated sfixed32 repeated_sfixed32 = 9 [packed = false]; + repeated sfixed64 repeated_sfixed64 = 10 [packed = false]; + repeated float repeated_float = 11 [packed = false]; + repeated double repeated_double = 12 [packed = false]; + repeated bool repeated_bool = 13 [packed = false]; + repeated TestAllTypes.NestedEnum repeated_nested_enum = 14 [packed = false]; +} + +// This proto includes a recusively nested message. +message NestedTestAllTypes { + NestedTestAllTypes child = 1; + TestAllTypes payload = 2; +} + +// Define these after TestAllTypes to make sure the compiler can handle +// that. +message ForeignMessage { + int32 c = 1; +} + +enum ForeignEnum { + FOREIGN_ZERO = 0; + FOREIGN_FOO = 4; + FOREIGN_BAR = 5; + FOREIGN_BAZ = 6; +} + +// TestEmptyMessage is used to test behavior of unknown fields. +message TestEmptyMessage { +} + diff --git a/src/google/protobuf/unittest_proto3_lite.proto b/src/google/protobuf/unittest_proto3_lite.proto new file mode 100644 index 000000000..874ade6c0 --- /dev/null +++ b/src/google/protobuf/unittest_proto3_lite.proto @@ -0,0 +1,206 @@ +// 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. + +syntax = "proto3"; + +option optimize_for = LITE_RUNTIME; + +import "google/protobuf/unittest_import.proto"; + +package proto3_lite_unittest; + +// This proto includes every type of field in both singular and repeated +// forms. +message TestAllTypes { + message NestedMessage { + // The field name "b" fails to compile in proto1 because it conflicts with + // a local variable named "b" in one of the generated methods. Doh. + // This file needs to compile in proto1 to test backwards-compatibility. + int32 bb = 1; + } + + enum NestedEnum { + ZERO = 0; + FOO = 1; + BAR = 2; + BAZ = 3; + NEG = -1; // Intentionally negative. + } + + // Singular + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + sint32 optional_sint32 = 5; + sint64 optional_sint64 = 6; + fixed32 optional_fixed32 = 7; + fixed64 optional_fixed64 = 8; + sfixed32 optional_sfixed32 = 9; + sfixed64 optional_sfixed64 = 10; + float optional_float = 11; + double optional_double = 12; + bool optional_bool = 13; + string optional_string = 14; + bytes optional_bytes = 15; + + // Groups are not allowed in proto3. + // optional group OptionalGroup = 16 { + // optional int32 a = 17; + // } + + NestedMessage optional_nested_message = 18; + ForeignMessage optional_foreign_message = 19; + protobuf_unittest_import.ImportMessage optional_import_message = 20; + + NestedEnum optional_nested_enum = 21; + ForeignEnum optional_foreign_enum = 22; + + // Omitted (compared to unittest.proto) because proto2 enums are not allowed + // inside proto2 messages. + // + // optional protobuf_unittest_import.ImportEnum optional_import_enum = 23; + + string optional_string_piece = 24 [ctype=STRING_PIECE]; + string optional_cord = 25 [ctype=CORD]; + + // Defined in unittest_import_public.proto + protobuf_unittest_import.PublicImportMessage + optional_public_import_message = 26; + + NestedMessage optional_lazy_message = 27 [lazy=true]; + + // Repeated + repeated int32 repeated_int32 = 31; + repeated int64 repeated_int64 = 32; + repeated uint32 repeated_uint32 = 33; + repeated uint64 repeated_uint64 = 34; + repeated sint32 repeated_sint32 = 35; + repeated sint64 repeated_sint64 = 36; + repeated fixed32 repeated_fixed32 = 37; + repeated fixed64 repeated_fixed64 = 38; + repeated sfixed32 repeated_sfixed32 = 39; + repeated sfixed64 repeated_sfixed64 = 40; + repeated float repeated_float = 41; + repeated double repeated_double = 42; + repeated bool repeated_bool = 43; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + + // Groups are not allowed in proto3. + // repeated group RepeatedGroup = 46 { + // optional int32 a = 47; + // } + + repeated NestedMessage repeated_nested_message = 48; + repeated ForeignMessage repeated_foreign_message = 49; + repeated protobuf_unittest_import.ImportMessage repeated_import_message = 50; + + repeated NestedEnum repeated_nested_enum = 51; + repeated ForeignEnum repeated_foreign_enum = 52; + + // Omitted (compared to unittest.proto) because proto2 enums are not allowed + // inside proto2 messages. + // + // repeated protobuf_unittest_import.ImportEnum repeated_import_enum = 53; + + repeated string repeated_string_piece = 54 [ctype=STRING_PIECE]; + repeated string repeated_cord = 55 [ctype=CORD]; + + repeated NestedMessage repeated_lazy_message = 57 [lazy=true]; + + oneof oneof_field { + uint32 oneof_uint32 = 111; + NestedMessage oneof_nested_message = 112; + string oneof_string = 113; + bytes oneof_bytes = 114; + } +} + +// Test messages for packed fields + +message TestPackedTypes { + repeated int32 packed_int32 = 90 [packed = true]; + repeated int64 packed_int64 = 91 [packed = true]; + repeated uint32 packed_uint32 = 92 [packed = true]; + repeated uint64 packed_uint64 = 93 [packed = true]; + repeated sint32 packed_sint32 = 94 [packed = true]; + repeated sint64 packed_sint64 = 95 [packed = true]; + repeated fixed32 packed_fixed32 = 96 [packed = true]; + repeated fixed64 packed_fixed64 = 97 [packed = true]; + repeated sfixed32 packed_sfixed32 = 98 [packed = true]; + repeated sfixed64 packed_sfixed64 = 99 [packed = true]; + repeated float packed_float = 100 [packed = true]; + repeated double packed_double = 101 [packed = true]; + repeated bool packed_bool = 102 [packed = true]; + repeated ForeignEnum packed_enum = 103 [packed = true]; +} + +// Explicitly set packed to false +message TestUnpackedTypes { + repeated int32 repeated_int32 = 1 [packed = false]; + repeated int64 repeated_int64 = 2 [packed = false]; + repeated uint32 repeated_uint32 = 3 [packed = false]; + repeated uint64 repeated_uint64 = 4 [packed = false]; + repeated sint32 repeated_sint32 = 5 [packed = false]; + repeated sint64 repeated_sint64 = 6 [packed = false]; + repeated fixed32 repeated_fixed32 = 7 [packed = false]; + repeated fixed64 repeated_fixed64 = 8 [packed = false]; + repeated sfixed32 repeated_sfixed32 = 9 [packed = false]; + repeated sfixed64 repeated_sfixed64 = 10 [packed = false]; + repeated float repeated_float = 11 [packed = false]; + repeated double repeated_double = 12 [packed = false]; + repeated bool repeated_bool = 13 [packed = false]; + repeated TestAllTypes.NestedEnum repeated_nested_enum = 14 [packed = false]; +} + +// This proto includes a recusively nested message. +message NestedTestAllTypes { + NestedTestAllTypes child = 1; + TestAllTypes payload = 2; +} + +// Define these after TestAllTypes to make sure the compiler can handle +// that. +message ForeignMessage { + int32 c = 1; +} + +enum ForeignEnum { + FOREIGN_ZERO = 0; + FOREIGN_FOO = 4; + FOREIGN_BAR = 5; + FOREIGN_BAZ = 6; +} + +// TestEmptyMessage is used to test behavior of unknown fields. +message TestEmptyMessage { +} + diff --git a/src/google/protobuf/util/field_comparator.cc b/src/google/protobuf/util/field_comparator.cc index 9f613265e..60b8b8a5a 100644 --- a/src/google/protobuf/util/field_comparator.cc +++ b/src/google/protobuf/util/field_comparator.cc @@ -92,7 +92,27 @@ FieldComparator::ComparisonResult DefaultFieldComparator::Compare( case FieldDescriptor::CPPTYPE_INT64: COMPARE_FIELD(Int64); case FieldDescriptor::CPPTYPE_STRING: - COMPARE_FIELD(String); + if (field->is_repeated()) { + // Allocate scratch strings to store the result if a conversion is + // needed. + string scratch1; + string scratch2; + return ResultFromBoolean( + CompareString(*field, reflection_1->GetRepeatedStringReference( + message_1, field, index_1, &scratch1), + reflection_2->GetRepeatedStringReference( + message_2, field, index_2, &scratch2))); + } else { + // Allocate scratch strings to store the result if a conversion is + // needed. + string scratch1; + string scratch2; + return ResultFromBoolean(CompareString( + *field, + reflection_1->GetStringReference(message_1, field, &scratch1), + reflection_2->GetStringReference(message_2, field, &scratch2))); + } + break; case FieldDescriptor::CPPTYPE_UINT32: COMPARE_FIELD(UInt32); case FieldDescriptor::CPPTYPE_UINT64: diff --git a/src/google/protobuf/util/field_comparator.h b/src/google/protobuf/util/field_comparator.h index 8b83c69f2..1b4d65b0e 100644 --- a/src/google/protobuf/util/field_comparator.h +++ b/src/google/protobuf/util/field_comparator.h @@ -91,9 +91,9 @@ class LIBPROTOBUF_EXPORT FieldComparator { GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldComparator); }; -// Basic implementation of FieldComparator. Supports four modes of floating +// Basic implementation of FieldComparator. Supports three modes of floating // point value comparison: exact, approximate using MathUtil::AlmostEqual -// method, and arbitrarilly precise using MathUtil::WithinFractionOrMargin. +// method, and arbitrarily precise using MathUtil::WithinFractionOrMargin. class LIBPROTOBUF_EXPORT DefaultFieldComparator : public FieldComparator { public: enum FloatComparison { diff --git a/src/google/protobuf/util/field_comparator_test.cc b/src/google/protobuf/util/field_comparator_test.cc index 23f7d51d3..6fd631d86 100644 --- a/src/google/protobuf/util/field_comparator_test.cc +++ b/src/google/protobuf/util/field_comparator_test.cc @@ -42,7 +42,6 @@ // and the opensource version gtest.h header includes cmath transitively // somehow. #include - namespace google { namespace protobuf { namespace util { diff --git a/src/google/protobuf/util/field_mask_util.cc b/src/google/protobuf/util/field_mask_util.cc index c59f43aa6..547c9fb52 100644 --- a/src/google/protobuf/util/field_mask_util.cc +++ b/src/google/protobuf/util/field_mask_util.cc @@ -52,6 +52,82 @@ void FieldMaskUtil::FromString(StringPiece str, FieldMask* out) { } } +bool FieldMaskUtil::SnakeCaseToCamelCase(StringPiece input, string* output) { + output->clear(); + bool after_underscore = false; + for (int i = 0; i < input.size(); ++i) { + if (input[i] >= 'A' && input[i] <= 'Z') { + // The field name must not contain uppercase letters. + return false; + } + if (after_underscore) { + if (input[i] >= 'a' && input[i] <= 'z') { + output->push_back(input[i] + 'A' - 'a'); + after_underscore = false; + } else { + // The character after a "_" must be a lowercase letter. + return false; + } + } else if (input[i] == '_') { + after_underscore = true; + } else { + output->push_back(input[i]); + } + } + if (after_underscore) { + // Trailing "_". + return false; + } + return true; +} + +bool FieldMaskUtil::CamelCaseToSnakeCase(StringPiece input, string* output) { + output->clear(); + for (int i = 0; i < input.size(); ++i) { + if (input[i] == '_') { + // The field name must not contain "_"s. + return false; + } + if (input[i] >= 'A' && input[i] <= 'Z') { + output->push_back('_'); + output->push_back(input[i] + 'a' - 'A'); + } else { + output->push_back(input[i]); + } + } + return true; +} + +bool FieldMaskUtil::ToJsonString(const FieldMask& mask, string* out) { + out->clear(); + for (int i = 0; i < mask.paths_size(); ++i) { + const string& path = mask.paths(i); + string camelcase_path; + if (!SnakeCaseToCamelCase(path, &camelcase_path)) { + return false; + } + if (i > 0) { + out->push_back(','); + } + out->append(camelcase_path); + } + return true; +} + +bool FieldMaskUtil::FromJsonString(StringPiece str, FieldMask* out) { + out->Clear(); + vector paths = Split(str, ","); + for (int i = 0; i < paths.size(); ++i) { + if (paths[i].empty()) continue; + string snakecase_path; + if (!CamelCaseToSnakeCase(paths[i], &snakecase_path)) { + return false; + } + out->add_paths(snakecase_path); + } + return true; +} + bool FieldMaskUtil::InternalIsValidPath(const Descriptor* descriptor, StringPiece path) { vector parts = Split(path, "."); diff --git a/src/google/protobuf/util/field_mask_util.h b/src/google/protobuf/util/field_mask_util.h index 92f69893a..644161b94 100644 --- a/src/google/protobuf/util/field_mask_util.h +++ b/src/google/protobuf/util/field_mask_util.h @@ -45,11 +45,18 @@ class LIBPROTOBUF_EXPORT FieldMaskUtil { typedef google::protobuf::FieldMask FieldMask; public: - // Converts FieldMask to/from string, formatted according to proto3 JSON - // spec for FieldMask (e.g., "foo,bar,baz.quz"). + // Converts FieldMask to/from string, formatted by separating each path + // with a comma (e.g., "foo_bar,baz.quz"). static string ToString(const FieldMask& mask); static void FromString(StringPiece str, FieldMask* out); + // Converts FieldMask to/from string, formatted according to proto3 JSON + // spec for FieldMask (e.g., "fooBar,baz.quz"). If the field name is not + // style conforming (i.e., not snake_case when converted to string, or not + // camelCase when converted from string), the conversion will fail. + static bool ToJsonString(const FieldMask& mask, string* out); + static bool FromJsonString(StringPiece str, FieldMask* out); + // Checks whether the given path is valid for type T. template static bool IsValidPath(StringPiece path) { @@ -105,6 +112,35 @@ class LIBPROTOBUF_EXPORT FieldMaskUtil { const MergeOptions& options, Message* destination); private: + friend class SnakeCaseCamelCaseTest; + // Converts a field name from snake_case to camelCase: + // 1. Every character after "_" will be converted to uppercase. + // 2. All "_"s are removed. + // The conversion will fail if: + // 1. The field name contains uppercase letters. + // 2. Any character after a "_" is not a lowercase letter. + // If the conversion succeeds, it's guaranteed that the resulted + // camelCase name will yield the original snake_case name when + // converted using CamelCaseToSnakeCase(). + // + // Note that the input can contain characters not allowed in C identifiers. + // For example, "foo_bar,baz_quz" will be converted to "fooBar,bazQuz" + // successfully. + static bool SnakeCaseToCamelCase(StringPiece input, string* output); + // Converts a field name from camelCase to snake_case: + // 1. Every uppercase letter is converted to lowercase with a additional + // preceding "-". + // The conversion will fail if: + // 1. The field name contains "_"s. + // If the conversion succeeds, it's guaranteed that the resulted + // snake_case name will yield the original camelCase name when + // converted using SnakeCaseToCamelCase(). + // + // Note that the input can contain characters not allowed in C identifiers. + // For example, "fooBar,bazQuz" will be converted to "foo_bar,baz_quz" + // successfully. + static bool CamelCaseToSnakeCase(StringPiece input, string* output); + static bool InternalIsValidPath(const Descriptor* descriptor, StringPiece path); diff --git a/src/google/protobuf/util/field_mask_util_test.cc b/src/google/protobuf/util/field_mask_util_test.cc index a9523250b..9b7fb62a7 100644 --- a/src/google/protobuf/util/field_mask_util_test.cc +++ b/src/google/protobuf/util/field_mask_util_test.cc @@ -30,6 +30,10 @@ #include +#include + +#include +#include #include #include #include @@ -38,8 +42,77 @@ namespace google { namespace protobuf { namespace util { + +class SnakeCaseCamelCaseTest : public ::testing::Test { + protected: + string SnakeCaseToCamelCase(const string& input) { + string output; + if (FieldMaskUtil::SnakeCaseToCamelCase(input, &output)) { + return output; + } else { + return "#FAIL#"; + } + } + + string CamelCaseToSnakeCase(const string& input) { + string output; + if (FieldMaskUtil::CamelCaseToSnakeCase(input, &output)) { + return output; + } else { + return "#FAIL#"; + } + } +}; + namespace { +TEST_F(SnakeCaseCamelCaseTest, SnakeToCamel) { + EXPECT_EQ("fooBar", SnakeCaseToCamelCase("foo_bar")); + EXPECT_EQ("FooBar", SnakeCaseToCamelCase("_foo_bar")); + EXPECT_EQ("foo3Bar", SnakeCaseToCamelCase("foo3_bar")); + // No uppercase letter is allowed. + EXPECT_EQ("#FAIL#", SnakeCaseToCamelCase("Foo")); + // Any character after a "_" must be a lowercase letter. + // 1. "_" cannot be followed by another "_". + // 2. "_" cannot be followed by a digit. + // 3. "_" cannot appear as the last character. + EXPECT_EQ("#FAIL#", SnakeCaseToCamelCase("foo__bar")); + EXPECT_EQ("#FAIL#", SnakeCaseToCamelCase("foo_3bar")); + EXPECT_EQ("#FAIL#", SnakeCaseToCamelCase("foo_bar_")); +} + +TEST_F(SnakeCaseCamelCaseTest, CamelToSnake) { + EXPECT_EQ("foo_bar", CamelCaseToSnakeCase("fooBar")); + EXPECT_EQ("_foo_bar", CamelCaseToSnakeCase("FooBar")); + EXPECT_EQ("foo3_bar", CamelCaseToSnakeCase("foo3Bar")); + // "_"s are not allowed. + EXPECT_EQ("#FAIL#", CamelCaseToSnakeCase("foo_bar")); +} + +TEST_F(SnakeCaseCamelCaseTest, RoundTripTest) { + // Enumerates all possible snake_case names and test that converting it to + // camelCase and then to snake_case again will yield the original name. + string name = "___abc123"; + std::sort(name.begin(), name.end()); + do { + string camelName = SnakeCaseToCamelCase(name); + if (camelName != "#FAIL#") { + EXPECT_EQ(name, CamelCaseToSnakeCase(camelName)); + } + } while (std::next_permutation(name.begin(), name.end())); + + // Enumerates all possible camelCase names and test that converting it to + // snake_case and then to camelCase again will yield the original name. + name = "abcABC123"; + std::sort(name.begin(), name.end()); + do { + string camelName = CamelCaseToSnakeCase(name); + if (camelName != "#FAIL#") { + EXPECT_EQ(name, SnakeCaseToCamelCase(camelName)); + } + } while (std::next_permutation(name.begin(), name.end())); +} + using protobuf_unittest::TestAllTypes; using protobuf_unittest::NestedTestAllTypes; using google::protobuf::FieldMask; @@ -47,20 +120,43 @@ using google::protobuf::FieldMask; TEST(FieldMaskUtilTest, StringFormat) { FieldMask mask; EXPECT_EQ("", FieldMaskUtil::ToString(mask)); - mask.add_paths("foo"); - EXPECT_EQ("foo", FieldMaskUtil::ToString(mask)); - mask.add_paths("bar"); - EXPECT_EQ("foo,bar", FieldMaskUtil::ToString(mask)); + mask.add_paths("foo_bar"); + EXPECT_EQ("foo_bar", FieldMaskUtil::ToString(mask)); + mask.add_paths("baz_quz"); + EXPECT_EQ("foo_bar,baz_quz", FieldMaskUtil::ToString(mask)); FieldMaskUtil::FromString("", &mask); EXPECT_EQ(0, mask.paths_size()); - FieldMaskUtil::FromString("foo", &mask); + FieldMaskUtil::FromString("fooBar", &mask); EXPECT_EQ(1, mask.paths_size()); - EXPECT_EQ("foo", mask.paths(0)); - FieldMaskUtil::FromString("foo,bar", &mask); + EXPECT_EQ("fooBar", mask.paths(0)); + FieldMaskUtil::FromString("fooBar,bazQuz", &mask); EXPECT_EQ(2, mask.paths_size()); - EXPECT_EQ("foo", mask.paths(0)); - EXPECT_EQ("bar", mask.paths(1)); + EXPECT_EQ("fooBar", mask.paths(0)); + EXPECT_EQ("bazQuz", mask.paths(1)); +} + +TEST(FieldMaskUtilTest, JsonStringFormat) { + FieldMask mask; + string value; + EXPECT_TRUE(FieldMaskUtil::ToJsonString(mask, &value)); + EXPECT_EQ("", value); + mask.add_paths("foo_bar"); + EXPECT_TRUE(FieldMaskUtil::ToJsonString(mask, &value)); + EXPECT_EQ("fooBar", value); + mask.add_paths("bar_quz"); + EXPECT_TRUE(FieldMaskUtil::ToJsonString(mask, &value)); + EXPECT_EQ("fooBar,barQuz", value); + + FieldMaskUtil::FromJsonString("", &mask); + EXPECT_EQ(0, mask.paths_size()); + FieldMaskUtil::FromJsonString("fooBar", &mask); + EXPECT_EQ(1, mask.paths_size()); + EXPECT_EQ("foo_bar", mask.paths(0)); + FieldMaskUtil::FromJsonString("fooBar,bazQuz", &mask); + EXPECT_EQ(2, mask.paths_size()); + EXPECT_EQ("foo_bar", mask.paths(0)); + EXPECT_EQ("baz_quz", mask.paths(1)); } TEST(FieldMaskUtilTest, TestIsVaildPath) { diff --git a/src/google/protobuf/util/internal/constants.h b/src/google/protobuf/util/internal/constants.h index 0cb8f6e1e..e556888cc 100644 --- a/src/google/protobuf/util/internal/constants.h +++ b/src/google/protobuf/util/internal/constants.h @@ -43,13 +43,23 @@ namespace converter { const char kTypeServiceBaseUrl[] = "type.googleapis.com"; // Format string for RFC3339 timestamp formatting. -const char kRfc3339TimeFormat[] = "%Y-%m-%dT%H:%M:%S"; +const char kRfc3339TimeFormat[] = "%E4Y-%m-%dT%H:%M:%S"; -// Minimum seconds allowed in a google.protobuf.TimeStamp or Duration value. -const int64 kMinSeconds = -315576000000; +// Same as above, but the year value is not zero-padded i.e. this accepts +// timestamps like "1-01-0001T23:59:59Z" instead of "0001-01-0001T23:59:59Z". +const char kRfc3339TimeFormatNoPadding[] = "%Y-%m-%dT%H:%M:%S"; -// Maximum seconds allowed in a google.protobuf.TimeStamp or Duration value. -const int64 kMaxSeconds = 315576000000; +// Minimun seconds allowed in a google.protobuf.Timestamp value. +const int64 kTimestampMinSeconds = -62135596800; + +// Maximum seconds allowed in a google.protobuf.Timestamp value. +const int64 kTimestampMaxSeconds = 253402300799; + +// Minimum seconds allowed in a google.protobuf.Duration value. +const int64 kDurationMinSeconds = -315576000000; + +// Maximum seconds allowed in a google.protobuf.Duration value. +const int64 kDurationMaxSeconds = 315576000000; // Nano seconds in a second. const int32 kNanosPerSecond = 1000000000; diff --git a/src/google/protobuf/util/internal/datapiece.cc b/src/google/protobuf/util/internal/datapiece.cc index b557429fc..72c0aca60 100644 --- a/src/google/protobuf/util/internal/datapiece.cc +++ b/src/google/protobuf/util/internal/datapiece.cc @@ -47,6 +47,7 @@ using google::protobuf::EnumDescriptor; using google::protobuf::EnumValueDescriptor; ; ; +; using util::error::Code; using util::Status; using util::StatusOr; @@ -248,11 +249,8 @@ StatusOr DataPiece::ToBytes() const { if (type_ == TYPE_BYTES) return str_.ToString(); if (type_ == TYPE_STRING) { string decoded; - if (!WebSafeBase64Unescape(str_, &decoded)) { - if (!Base64Unescape(str_, &decoded)) { - return InvalidArgument( - ValueAsStringOrDefault("Invalid data in input.")); - } + if (!DecodeBase64(str_, &decoded)) { + return InvalidArgument(ValueAsStringOrDefault("Invalid data in input.")); } return decoded; } else { @@ -313,11 +311,49 @@ StatusOr DataPiece::GenericConvert() const { template StatusOr DataPiece::StringToNumber(bool (*func)(StringPiece, To*)) const { + if (str_.size() > 0 && (str_[0] == ' ' || str_[str_.size() - 1] == ' ')) { + return InvalidArgument(StrCat("\"", str_, "\"")); + } To result; if (func(str_, &result)) return result; return InvalidArgument(StrCat("\"", str_.ToString(), "\"")); } +bool DataPiece::DecodeBase64(StringPiece src, string* dest) const { + // Try web-safe decode first, if it fails, try the non-web-safe decode. + if (WebSafeBase64Unescape(src, dest)) { + if (use_strict_base64_decoding_) { + // In strict mode, check if the escaped version gives us the same value as + // unescaped. + string encoded; + // WebSafeBase64Escape does no padding by default. + WebSafeBase64Escape(*dest, &encoded); + // Remove trailing padding '=' characters before comparison. + StringPiece src_no_padding(src, 0, src.ends_with("=") + ? src.find_last_not_of('=') + 1 + : src.length()); + return encoded == src_no_padding; + } + return true; + } + + if (Base64Unescape(src, dest)) { + if (use_strict_base64_decoding_) { + string encoded; + Base64Escape( + reinterpret_cast(dest->data()), dest->length(), + &encoded, false); + StringPiece src_no_padding(src, 0, src.ends_with("=") + ? src.find_last_not_of('=') + 1 + : src.length()); + return encoded == src_no_padding; + } + return true; + } + + return false; +} + } // namespace converter } // namespace util } // namespace protobuf diff --git a/src/google/protobuf/util/internal/datapiece.h b/src/google/protobuf/util/internal/datapiece.h index f22bfe70d..8b2e35d3a 100644 --- a/src/google/protobuf/util/internal/datapiece.h +++ b/src/google/protobuf/util/internal/datapiece.h @@ -83,12 +83,15 @@ class LIBPROTOBUF_EXPORT DataPiece { explicit DataPiece(const double value) : type_(TYPE_DOUBLE), double_(value) {} explicit DataPiece(const float value) : type_(TYPE_FLOAT), float_(value) {} explicit DataPiece(const bool value) : type_(TYPE_BOOL), bool_(value) {} - explicit DataPiece(StringPiece value) + DataPiece(StringPiece value, bool use_strict_base64_decoding) : type_(TYPE_STRING), - str_(StringPiecePod::CreateFromStringPiece(value)) {} + str_(StringPiecePod::CreateFromStringPiece(value)), + use_strict_base64_decoding_(use_strict_base64_decoding) {} // Constructor for bytes. The second parameter is not used. - explicit DataPiece(StringPiece value, bool dummy) - : type_(TYPE_BYTES), str_(StringPiecePod::CreateFromStringPiece(value)) {} + DataPiece(StringPiece value, bool dummy, bool use_strict_base64_decoding) + : type_(TYPE_BYTES), + str_(StringPiecePod::CreateFromStringPiece(value)), + use_strict_base64_decoding_(use_strict_base64_decoding) {} DataPiece(const DataPiece& r) : type_(r.type_), str_(r.str_) {} DataPiece& operator=(const DataPiece& x) { type_ = x.type_; @@ -165,32 +168,13 @@ class LIBPROTOBUF_EXPORT DataPiece { template util::StatusOr StringToNumber(bool (*func)(StringPiece, To*)) const; + // Decodes a base64 string. Returns true on success. + bool DecodeBase64(StringPiece src, string* dest) const; + // Data type for this piece of data. Type type_; - // StringPiece is not a POD and can not be used in an union (pre C++11). We - // need a POD version of it. - struct StringPiecePod { - const char* data; - int size; - - // Create from a StringPiece. - static StringPiecePod CreateFromStringPiece(StringPiece str) { - StringPiecePod pod; - pod.data = str.data(); - pod.size = str.size(); - return pod; - } - - // Cast to StringPiece. - operator StringPiece() const { return StringPiece(data, size); } - - bool operator==(const char* value) const { - return StringPiece(data, size) == StringPiece(value); - } - - string ToString() const { return string(data, size); } - }; + typedef ::google::protobuf::internal::StringPiecePod StringPiecePod; // Stored piece of data. union { @@ -203,6 +187,9 @@ class LIBPROTOBUF_EXPORT DataPiece { bool bool_; StringPiecePod str_; }; + + // Uses a stricter version of base64 decoding for byte fields. + bool use_strict_base64_decoding_; }; } // namespace converter diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.cc b/src/google/protobuf/util/internal/default_value_objectwriter.cc index a63e560d3..21d7a2e4a 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter.cc +++ b/src/google/protobuf/util/internal/default_value_objectwriter.cc @@ -51,7 +51,7 @@ template T ConvertTo(StringPiece value, StatusOr (DataPiece::*converter_fn)() const, T default_value) { if (value.empty()) return default_value; - StatusOr result = (DataPiece(value).*converter_fn)(); + StatusOr result = (DataPiece(value, true).*converter_fn)(); return result.ok() ? result.ValueOrDie() : default_value; } } // namespace @@ -64,6 +64,7 @@ DefaultValueObjectWriter::DefaultValueObjectWriter( type_(type), current_(NULL), root_(NULL), + field_scrub_callback_(NULL), ow_(ow) {} DefaultValueObjectWriter::~DefaultValueObjectWriter() { @@ -153,7 +154,7 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::RenderString( // Since StringPiece is essentially a pointer, takes a copy of "value" to // avoid ownership issues. string_values_.push_back(new string(value.ToString())); - RenderDataPiece(name, DataPiece(*string_values_.back())); + RenderDataPiece(name, DataPiece(*string_values_.back(), true)); } return this; } @@ -163,7 +164,7 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBytes( if (current_ == NULL) { ow_->RenderBytes(name, value); } else { - RenderDataPiece(name, DataPiece(value)); + RenderDataPiece(name, DataPiece(value, false, true)); } return this; } @@ -178,16 +179,25 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::RenderNull( return this; } +void DefaultValueObjectWriter::RegisterFieldScrubCallBack( + FieldScrubCallBackPtr field_scrub_callback) { + field_scrub_callback_.reset(field_scrub_callback.release()); +} + DefaultValueObjectWriter::Node::Node(const string& name, const google::protobuf::Type* type, NodeKind kind, const DataPiece& data, - bool is_placeholder) + bool is_placeholder, + const vector& path, + FieldScrubCallBack* field_scrub_callback) : name_(name), type_(type), kind_(kind), is_any_(false), data_(data), - is_placeholder_(is_placeholder) {} + is_placeholder_(is_placeholder), + path_(path), + field_scrub_callback_(field_scrub_callback) {} DefaultValueObjectWriter::Node* DefaultValueObjectWriter::Node::FindChild( StringPiece name) { @@ -291,6 +301,19 @@ void DefaultValueObjectWriter::Node::PopulateChildren( for (int i = 0; i < type_->fields_size(); ++i) { const google::protobuf::Field& field = type_->fields(i); + + // This code is checking if the field to be added to the tree should be + // scrubbed or not by calling the field_scrub_callback_ callback function. + vector path; + if (!path_.empty()) { + path.insert(path.begin(), path_.begin(), path_.end()); + } + path.push_back(field.name()); + if (field_scrub_callback_ != NULL && + field_scrub_callback_->Run(path, &field)) { + continue; + } + hash_map::iterator found = orig_children_map.find(field.name()); // If the child field has already been set, we just add it to the new list @@ -343,7 +366,7 @@ void DefaultValueObjectWriter::Node::PopulateChildren( field.json_name(), field_type, kind, kind == PRIMITIVE ? CreateDefaultDataPieceForField(field, typeinfo) : DataPiece::NullData(), - true)); + true, path, field_scrub_callback_)); new_children.push_back(child.release()); } // Adds all leftover nodes in children_ to the beginning of new_child. @@ -368,7 +391,8 @@ void DefaultValueObjectWriter::MaybePopulateChildrenOfAny(Node* node) { DataPiece DefaultValueObjectWriter::FindEnumDefault( const google::protobuf::Field& field, const TypeInfo* typeinfo) { - if (!field.default_value().empty()) return DataPiece(field.default_value()); + if (!field.default_value().empty()) + return DataPiece(field.default_value(), true); const google::protobuf::Enum* enum_type = typeinfo->GetEnumByTypeUrl(field.type_url()); @@ -379,7 +403,7 @@ DataPiece DefaultValueObjectWriter::FindEnumDefault( } // We treat the first value as the default if none is specified. return enum_type->enumvalue_size() > 0 - ? DataPiece(enum_type->enumvalue(0).name()) + ? DataPiece(enum_type->enumvalue(0).name(), true) : DataPiece::NullData(); } @@ -416,10 +440,10 @@ DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField( ConvertTo(field.default_value(), &DataPiece::ToBool, false)); } case google::protobuf::Field_Kind_TYPE_STRING: { - return DataPiece(field.default_value()); + return DataPiece(field.default_value(), true); } case google::protobuf::Field_Kind_TYPE_BYTES: { - return DataPiece(field.default_value(), false); + return DataPiece(field.default_value(), false, true); } case google::protobuf::Field_Kind_TYPE_UINT32: case google::protobuf::Field_Kind_TYPE_FIXED32: { @@ -436,8 +460,9 @@ DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField( DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( StringPiece name) { if (current_ == NULL) { + vector path; root_.reset(new Node(name.ToString(), &type_, OBJECT, DataPiece::NullData(), - false)); + false, path, field_scrub_callback_.get())); root_->PopulateChildren(typeinfo_); current_ = root_.get(); return this; @@ -451,7 +476,9 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( name.ToString(), ((current_->kind() == LIST || current_->kind() == MAP) ? current_->type() : NULL), - OBJECT, DataPiece::NullData(), false)); + OBJECT, DataPiece::NullData(), false, + child == NULL ? current_->path() : child->path(), + field_scrub_callback_.get())); child = node.get(); current_->AddChild(node.release()); } @@ -480,8 +507,9 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::EndObject() { DefaultValueObjectWriter* DefaultValueObjectWriter::StartList( StringPiece name) { if (current_ == NULL) { - root_.reset( - new Node(name.ToString(), &type_, LIST, DataPiece::NullData(), false)); + vector path; + root_.reset(new Node(name.ToString(), &type_, LIST, DataPiece::NullData(), + false, path, field_scrub_callback_.get())); current_ = root_.get(); return this; } @@ -489,7 +517,9 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartList( Node* child = current_->FindChild(name); if (child == NULL || child->kind() != LIST) { google::protobuf::scoped_ptr node( - new Node(name.ToString(), NULL, LIST, DataPiece::NullData(), false)); + new Node(name.ToString(), NULL, LIST, DataPiece::NullData(), false, + child == NULL ? current_->path() : child->path(), + field_scrub_callback_.get())); child = node.get(); current_->AddChild(node.release()); } @@ -545,7 +575,9 @@ void DefaultValueObjectWriter::RenderDataPiece(StringPiece name, if (child == NULL || child->kind() != PRIMITIVE) { // No children are found, creates a new child. google::protobuf::scoped_ptr node( - new Node(name.ToString(), NULL, PRIMITIVE, data, false)); + new Node(name.ToString(), NULL, PRIMITIVE, data, false, + child == NULL ? current_->path() : child->path(), + field_scrub_callback_.get())); child = node.get(); current_->AddChild(node.release()); } else { diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.h b/src/google/protobuf/util/internal/default_value_objectwriter.h index 695b9dd8a..1d85bed85 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter.h +++ b/src/google/protobuf/util/internal/default_value_objectwriter.h @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -59,6 +60,25 @@ namespace converter { // with their default values (0 for numbers, "" for strings, etc). class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { public: + // A Callback function to check whether a field needs to be scrubbed. + // + // Returns true if the field should not be present in the output. Returns + // false otherwise. + // + // The 'path' parameter is a vector of path to the field from root. For + // example: if a nested field "a.b.c" (b is the parent message field of c and + // a is the parent message field of b), then the vector should contain { "a", + // "b", "c" }. + // + // The Field* should point to the google::protobuf::Field of "c". + typedef ResultCallback2& /*path of the field*/, + const google::protobuf::Field* /*field*/> + FieldScrubCallBack; + + // A unique pointer to a DefaultValueObjectWriter::FieldScrubCallBack. + typedef google::protobuf::scoped_ptr FieldScrubCallBackPtr; + DefaultValueObjectWriter(TypeResolver* type_resolver, const google::protobuf::Type& type, ObjectWriter* ow); @@ -98,6 +118,10 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { virtual DefaultValueObjectWriter* RenderNull(StringPiece name); + // Register the callback for scrubbing of fields. Owership of + // field_scrub_callback pointer is also transferred to this class + void RegisterFieldScrubCallBack(FieldScrubCallBackPtr field_scrub_callback); + private: enum NodeKind { PRIMITIVE = 0, @@ -111,7 +135,8 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { class LIBPROTOBUF_EXPORT Node { public: Node(const string& name, const google::protobuf::Type* type, NodeKind kind, - const DataPiece& data, bool is_placeholder); + const DataPiece& data, bool is_placeholder, const vector& path, + FieldScrubCallBack* field_scrub_callback); virtual ~Node() { for (int i = 0; i < children_.size(); ++i) { delete children_[i]; @@ -137,17 +162,19 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // Accessors const string& name() const { return name_; } - const google::protobuf::Type* type() { return type_; } + const vector& path() const { return path_; } + + const google::protobuf::Type* type() const { return type_; } void set_type(const google::protobuf::Type* type) { type_ = type; } - NodeKind kind() { return kind_; } + NodeKind kind() const { return kind_; } - int number_of_children() { return children_.size(); } + int number_of_children() const { return children_.size(); } void set_data(const DataPiece& data) { data_ = data; } - bool is_any() { return is_any_; } + bool is_any() const { return is_any_; } void set_is_any(bool is_any) { is_any_ = is_any; } @@ -181,6 +208,15 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // the parent node's StartObject()/StartList() method is called with this // node's name. bool is_placeholder_; + + // Path of the field of this node + std::vector path_; + + // Pointer to function for determining whether a field needs to be scrubbed + // or not. This callback is owned by the creator of this node. + FieldScrubCallBack* field_scrub_callback_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Node); }; // Populates children of "node" if it is an "any" Node and its real type has @@ -221,6 +257,10 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // The stack to hold the path of Nodes from current_ to root_; std::stack stack_; + // Unique Pointer to function for determining whether a field needs to be + // scrubbed or not. + FieldScrubCallBackPtr field_scrub_callback_; + ObjectWriter* ow_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DefaultValueObjectWriter); diff --git a/src/google/protobuf/util/internal/json_objectwriter.cc b/src/google/protobuf/util/internal/json_objectwriter.cc index 94d2ab7a4..b84210c10 100644 --- a/src/google/protobuf/util/internal/json_objectwriter.cc +++ b/src/google/protobuf/util/internal/json_objectwriter.cc @@ -46,6 +46,7 @@ namespace util { namespace converter { using strings::ArrayByteSource; +; JsonObjectWriter::~JsonObjectWriter() { if (!element_->is_root()) { @@ -81,13 +82,11 @@ JsonObjectWriter* JsonObjectWriter::EndList() { return this; } -JsonObjectWriter* JsonObjectWriter::RenderBool(StringPiece name, - bool value) { +JsonObjectWriter* JsonObjectWriter::RenderBool(StringPiece name, bool value) { return RenderSimple(name, value ? "true" : "false"); } -JsonObjectWriter* JsonObjectWriter::RenderInt32(StringPiece name, - int32 value) { +JsonObjectWriter* JsonObjectWriter::RenderInt32(StringPiece name, int32 value) { return RenderSimple(name, SimpleItoa(value)); } @@ -96,8 +95,7 @@ JsonObjectWriter* JsonObjectWriter::RenderUint32(StringPiece name, return RenderSimple(name, SimpleItoa(value)); } -JsonObjectWriter* JsonObjectWriter::RenderInt64(StringPiece name, - int64 value) { +JsonObjectWriter* JsonObjectWriter::RenderInt64(StringPiece name, int64 value) { WritePrefix(name); WriteChar('"'); stream_->WriteString(SimpleItoa(value)); @@ -124,8 +122,7 @@ JsonObjectWriter* JsonObjectWriter::RenderDouble(StringPiece name, return RenderString(name, DoubleAsString(value)); } -JsonObjectWriter* JsonObjectWriter::RenderFloat(StringPiece name, - float value) { +JsonObjectWriter* JsonObjectWriter::RenderFloat(StringPiece name, float value) { if (MathLimits::IsFinite(value)) { return RenderSimple(name, SimpleFtoa(value)); } @@ -148,7 +145,12 @@ JsonObjectWriter* JsonObjectWriter::RenderBytes(StringPiece name, StringPiece value) { WritePrefix(name); string base64; - Base64Escape(value, &base64); + + if (use_websafe_base64_for_bytes_) + WebSafeBase64Escape(value.ToString(), &base64); + else + Base64Escape(value, &base64); + WriteChar('"'); // TODO(wpoon): Consider a ByteSink solution that writes the base64 bytes // directly to the stream, rather than first putting them diff --git a/src/google/protobuf/util/internal/json_objectwriter.h b/src/google/protobuf/util/internal/json_objectwriter.h index 761a0a108..cb7e2fb30 100644 --- a/src/google/protobuf/util/internal/json_objectwriter.h +++ b/src/google/protobuf/util/internal/json_objectwriter.h @@ -90,9 +90,10 @@ class LIBPROTOBUF_EXPORT JsonObjectWriter : public StructuredObjectWriter { JsonObjectWriter(StringPiece indent_string, google::protobuf::io::CodedOutputStream* out) : element_(new Element(NULL)), - stream_(out), sink_(out), - indent_string_(indent_string.ToString()) { - } + stream_(out), + sink_(out), + indent_string_(indent_string.ToString()), + use_websafe_base64_for_bytes_(false) {} virtual ~JsonObjectWriter(); // ObjectWriter methods. @@ -111,6 +112,10 @@ class LIBPROTOBUF_EXPORT JsonObjectWriter : public StructuredObjectWriter { virtual JsonObjectWriter* RenderBytes(StringPiece name, StringPiece value); virtual JsonObjectWriter* RenderNull(StringPiece name); + void set_use_websafe_base64_for_bytes(bool value) { + use_websafe_base64_for_bytes_ = value; + } + protected: class LIBPROTOBUF_EXPORT Element : public BaseElement { public: @@ -195,6 +200,10 @@ class LIBPROTOBUF_EXPORT JsonObjectWriter : public StructuredObjectWriter { ByteSinkWrapper sink_; const string indent_string_; + // Whether to use regular or websafe base64 encoding for byte fields. Defaults + // to regular base64 encoding. + bool use_websafe_base64_for_bytes_; + GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(JsonObjectWriter); }; diff --git a/src/google/protobuf/util/internal/json_objectwriter_test.cc b/src/google/protobuf/util/internal/json_objectwriter_test.cc index 9d8201624..b87b06ac7 100644 --- a/src/google/protobuf/util/internal/json_objectwriter_test.cc +++ b/src/google/protobuf/util/internal/json_objectwriter_test.cc @@ -58,7 +58,7 @@ class JsonObjectWriterTest : public ::testing::Test { string output_; StringOutputStream* const str_stream_; CodedOutputStream* const out_stream_; - ObjectWriter* ow_; + JsonObjectWriter* ow_; }; TEST_F(JsonObjectWriterTest, EmptyRootObject) { @@ -283,6 +283,30 @@ TEST_F(JsonObjectWriterTest, Stringification) { output_.substr(0, out_stream_->ByteCount())); } +TEST_F(JsonObjectWriterTest, TestRegularByteEncoding) { + ow_ = new JsonObjectWriter("", out_stream_); + ow_->StartObject("") + ->RenderBytes("bytes", "\x03\xef\xc0") + ->EndObject(); + + // Test that we get regular (non websafe) base64 encoding on byte fields by + // default. + EXPECT_EQ("{\"bytes\":\"A+/A\"}", + output_.substr(0, out_stream_->ByteCount())); +} + +TEST_F(JsonObjectWriterTest, TestWebsafeByteEncoding) { + ow_ = new JsonObjectWriter("", out_stream_); + ow_->set_use_websafe_base64_for_bytes(true); + ow_->StartObject("") + ->RenderBytes("bytes", "\x03\xef\xc0") + ->EndObject(); + + // Test that we get websafe base64 encoding when explicitly asked. + EXPECT_EQ("{\"bytes\":\"A-_A\"}", + output_.substr(0, out_stream_->ByteCount())); +} + } // namespace converter } // namespace util } // namespace protobuf diff --git a/src/google/protobuf/util/internal/json_stream_parser.cc b/src/google/protobuf/util/internal/json_stream_parser.cc index df9167513..39be7b039 100644 --- a/src/google/protobuf/util/internal/json_stream_parser.cc +++ b/src/google/protobuf/util/internal/json_stream_parser.cc @@ -42,8 +42,9 @@ #include #include -#include #include +#include +#include namespace google { namespace protobuf { @@ -59,7 +60,7 @@ using util::error::INVALID_ARGUMENT; namespace converter { -// Number of digits in a unicode escape sequence (/uXXXX) +// Number of digits in an escaped UTF-16 code unit ('\\' 'u' X X X X) static const int kUnicodeEscapedLength = 6; // Length of the true, false, and null literals. @@ -419,9 +420,45 @@ util::Status JsonStreamParser::ParseUnicodeEscape() { } code = (code << 4) + hex_digit_to_int(p_.data()[i]); } + if (code >= JsonEscaping::kMinHighSurrogate && + code <= JsonEscaping::kMaxHighSurrogate) { + if (p_.length() < 2 * kUnicodeEscapedLength) { + if (!finishing_) { + return util::Status::CANCELLED; + } + if (!coerce_to_utf8_) { + return ReportFailure("Missing low surrogate."); + } + } else if (p_.data()[kUnicodeEscapedLength] == '\\' && + p_.data()[kUnicodeEscapedLength + 1] == 'u') { + uint32 low_code = 0; + for (int i = kUnicodeEscapedLength + 2; i < 2 * kUnicodeEscapedLength; + ++i) { + if (!isxdigit(p_.data()[i])) { + return ReportFailure("Invalid escape sequence."); + } + low_code = (low_code << 4) + hex_digit_to_int(p_.data()[i]); + } + if (low_code >= JsonEscaping::kMinLowSurrogate && + low_code <= JsonEscaping::kMaxLowSurrogate) { + // Convert UTF-16 surrogate pair to 21-bit Unicode codepoint. + code = (((code & 0x3FF) << 10) | (low_code & 0x3FF)) + + JsonEscaping::kMinSupplementaryCodePoint; + // Advance past the first code unit escape. + p_.remove_prefix(kUnicodeEscapedLength); + } else if (!coerce_to_utf8_) { + return ReportFailure("Invalid low surrogate."); + } + } else if (!coerce_to_utf8_) { + return ReportFailure("Missing low surrogate."); + } + } + if (!coerce_to_utf8_ && !IsValidCodePoint(code)) { + return ReportFailure("Invalid unicode code point."); + } char buf[UTFmax]; int len = EncodeAsUTF8Char(code, buf); - // Advance past the unicode escape. + // Advance past the [final] code unit escape. p_.remove_prefix(kUnicodeEscapedLength); parsed_storage_.append(buf, len); return util::Status::OK; @@ -473,7 +510,7 @@ util::Status JsonStreamParser::ParseNumberHelper(NumberResult* result) { floating = true; continue; } - if (c == '+' || c == '-') continue; + if (c == '+' || c == '-' || c == 'x') continue; // Not a valid number character, break out. break; } @@ -499,6 +536,10 @@ util::Status JsonStreamParser::ParseNumberHelper(NumberResult* result) { // Positive non-floating point number, parse as a uint64. if (!negative) { + // Octal/Hex numbers are not valid JSON values. + if (number.length() >= 2 && number[0] == '0') { + return ReportFailure("Octal/hex numbers are not valid JSON values."); + } if (!safe_strtou64(number, &result->uint_val)) { return ReportFailure("Unable to parse number."); } @@ -507,6 +548,10 @@ util::Status JsonStreamParser::ParseNumberHelper(NumberResult* result) { return util::Status::OK; } + // Octal/Hex numbers are not valid JSON values. + if (number.length() >= 3 && number[1] == '0') { + return ReportFailure("Octal/hex numbers are not valid JSON values."); + } // Negative non-floating point number, parse as an int64. if (!safe_strto64(number, &result->int_val)) { return ReportFailure("Unable to parse number."); @@ -677,8 +722,9 @@ util::Status JsonStreamParser::ReportFailure(StringPiece message) { static const int kContextLength = 20; const char* p_start = p_.data(); const char* json_start = json_.data(); - const char* begin = max(p_start - kContextLength, json_start); - const char* end = min(p_start + kContextLength, json_start + json_.size()); + const char* begin = std::max(p_start - kContextLength, json_start); + const char* end = + std::min(p_start + kContextLength, json_start + json_.size()); StringPiece segment(begin, end - begin); string location(p_start - begin, ' '); location.push_back('^'); @@ -706,8 +752,8 @@ void JsonStreamParser::SkipWhitespace() { void JsonStreamParser::Advance() { // Advance by moving one UTF8 character while making sure we don't go beyond // the length of StringPiece. - p_.remove_prefix( - min(p_.length(), UTF8FirstLetterNumBytes(p_.data(), p_.length()))); + p_.remove_prefix(std::min( + p_.length(), UTF8FirstLetterNumBytes(p_.data(), p_.length()))); } util::Status JsonStreamParser::ParseKey() { diff --git a/src/google/protobuf/util/internal/json_stream_parser_test.cc b/src/google/protobuf/util/internal/json_stream_parser_test.cc index 3414826ec..059ea6d86 100644 --- a/src/google/protobuf/util/internal/json_stream_parser_test.cc +++ b/src/google/protobuf/util/internal/json_stream_parser_test.cc @@ -124,8 +124,9 @@ class JsonStreamParserTest : public ::testing::Test { EXPECT_OK(result); } - void DoErrorTest(StringPiece json, int split, StringPiece error_prefix) { - util::Status result = RunTest(json, split); + void DoErrorTest(StringPiece json, int split, StringPiece error_prefix, + bool coerce_utf8 = false) { + util::Status result = RunTest(json, split, coerce_utf8); EXPECT_EQ(util::error::INVALID_ARGUMENT, result.error_code()); StringPiece error_message(result.error_message()); EXPECT_EQ(error_prefix, error_message.substr(0, error_prefix.size())); @@ -230,6 +231,32 @@ TEST_F(JsonStreamParserTest, SimpleUnsignedInt) { } } +TEST_F(JsonStreamParserTest, OctalNumberIsInvalid) { + StringPiece str = "01234"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Octal/hex numbers are not valid JSON values."); + } + str = "-01234"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Octal/hex numbers are not valid JSON values."); + } +} + +TEST_F(JsonStreamParserTest, HexNumberIsInvalid) { + StringPiece str = "0x1234"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Octal/hex numbers are not valid JSON values."); + } + str = "-0x1234"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Octal/hex numbers are not valid JSON values."); + } + str = "12x34"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Unable to parse number."); + } +} + // - single and double quoted strings TEST_F(JsonStreamParserTest, EmptyDoubleQuotedString) { StringPiece str = "\"\""; @@ -351,19 +378,62 @@ TEST_F(JsonStreamParserTest, RejectNonUtf8WhenNotCoerced) { DoErrorTest("\xFF{}", 0, "Encountered non UTF-8 code points."); } -#ifndef _MSC_VER // - unicode handling in strings TEST_F(JsonStreamParserTest, UnicodeEscaping) { StringPiece str = "[\"\\u0639\\u0631\\u0628\\u0649\"]"; for (int i = 0; i <= str.length(); ++i) { - // TODO(xiaofeng): Figure out what default encoding to use for JSON strings. - // In protobuf we use UTF-8 for strings, but for JSON we probably should - // allow different encodings? - ow_.StartList("")->RenderString("", "\u0639\u0631\u0628\u0649")->EndList(); + ow_.StartList("") + ->RenderString("", "\xD8\xB9\xD8\xB1\xD8\xA8\xD9\x89") + ->EndList(); DoTest(str, i); } } -#endif + +// - unicode UTF-16 surrogate pair handling in strings +TEST_F(JsonStreamParserTest, UnicodeSurrogatePairEscaping) { + StringPiece str = + "[\"\\u0bee\\ud800\\uddf1\\uD80C\\uDDA4\\uD83d\\udC1D\\uD83C\\uDF6F\"]"; + for (int i = 0; i <= str.length(); ++i) { + ow_.StartList("") + ->RenderString("", + "\xE0\xAF\xAE\xF0\x90\x87\xB1\xF0\x93\x86\xA4\xF0" + "\x9F\x90\x9D\xF0\x9F\x8D\xAF") + ->EndList(); + DoTest(str, i); + } +} + + +TEST_F(JsonStreamParserTest, UnicodeEscapingInvalidCodePointWhenNotCoerced) { + // A low surrogate alone. + StringPiece str = "[\"\\ude36\"]"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Invalid unicode code point."); + } +} + +TEST_F(JsonStreamParserTest, UnicodeEscapingMissingLowSurrogateWhenNotCoerced) { + // A high surrogate alone. + StringPiece str = "[\"\\ud83d\"]"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Missing low surrogate."); + } + // A high surrogate with some trailing characters. + str = "[\"\\ud83d|ude36\"]"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Missing low surrogate."); + } + // A high surrogate with half a low surrogate. + str = "[\"\\ud83d\\ude--\"]"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Invalid escape sequence."); + } + // Two high surrogates. + str = "[\"\\ud83d\\ud83d\"]"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Invalid low surrogate."); + } +} // - ascii escaping (\b, \f, \n, \r, \t, \v) TEST_F(JsonStreamParserTest, AsciiEscaping) { @@ -629,6 +699,22 @@ TEST_F(JsonStreamParserTest, DoubleTooBig) { } */ +// invalid bare backslash. +TEST_F(JsonStreamParserTest, UnfinishedEscape) { + StringPiece str = "\"\\"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Closing quote expected in string."); + } +} + +// invalid bare backslash u. +TEST_F(JsonStreamParserTest, UnfinishedUnicodeEscape) { + StringPiece str = "\"\\u"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Illegal hex string."); + } +} + // invalid unicode sequence. TEST_F(JsonStreamParserTest, UnicodeEscapeCutOff) { StringPiece str = "\"\\u12"; @@ -637,6 +723,15 @@ TEST_F(JsonStreamParserTest, UnicodeEscapeCutOff) { } } +// invalid unicode sequence (valid in modern EcmaScript but not in JSON). +TEST_F(JsonStreamParserTest, BracketedUnicodeEscape) { + StringPiece str = "\"\\u{1f36f}\""; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Invalid escape sequence."); + } +} + + TEST_F(JsonStreamParserTest, UnicodeEscapeInvalidCharacters) { StringPiece str = "\"\\u12$4hello"; for (int i = 0; i <= str.length(); ++i) { @@ -644,6 +739,14 @@ TEST_F(JsonStreamParserTest, UnicodeEscapeInvalidCharacters) { } } +// invalid unicode sequence in low half surrogate: g is not a hex digit. +TEST_F(JsonStreamParserTest, UnicodeEscapeLowHalfSurrogateInvalidCharacters) { + StringPiece str = "\"\\ud800\\udcfg\""; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Invalid escape sequence."); + } +} + // Extra commas with an object or array. TEST_F(JsonStreamParserTest, ExtraCommaInObject) { StringPiece str = "{'k1': true,,'k2': false}"; diff --git a/src/google/protobuf/util/internal/object_writer.h b/src/google/protobuf/util/internal/object_writer.h index e695f45e4..9f07363d8 100644 --- a/src/google/protobuf/util/internal/object_writer.h +++ b/src/google/protobuf/util/internal/object_writer.h @@ -105,10 +105,27 @@ class LIBPROTOBUF_EXPORT ObjectWriter { static void RenderDataPieceTo(const DataPiece& data, StringPiece name, ObjectWriter* ow); + // Indicates whether this ObjectWriter has completed writing the root message, + // usually this means writing of one complete object. Subclasses must override + // this behavior appropriately. + virtual bool done() { return false; } + + void set_use_strict_base64_decoding(bool value) { + use_strict_base64_decoding_ = value; + } + + bool use_strict_base64_decoding() const { + return use_strict_base64_decoding_; + } + protected: - ObjectWriter() {} + ObjectWriter() : use_strict_base64_decoding_(true) {} private: + // If set to true, we use the stricter version of base64 decoding for byte + // fields by making sure decoded version encodes back to the original string. + bool use_strict_base64_decoding_; + // Do not add any data members to this class. GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ObjectWriter); }; diff --git a/src/google/protobuf/util/internal/proto_writer.cc b/src/google/protobuf/util/internal/proto_writer.cc index 47e0009ec..36b794109 100644 --- a/src/google/protobuf/util/internal/proto_writer.cc +++ b/src/google/protobuf/util/internal/proto_writer.cc @@ -447,9 +447,7 @@ ProtoWriter* ProtoWriter::StartObject(StringPiece name) { return this; } - WriteTag(*field); - element_.reset(new ProtoElement(element_.release(), field, *type, false)); - return this; + return StartObjectField(*field, *type); } ProtoWriter* ProtoWriter::EndObject() { @@ -488,8 +486,7 @@ ProtoWriter* ProtoWriter::StartList(StringPiece name) { return this; } - element_.reset(new ProtoElement(element_.release(), field, *type, true)); - return this; + return StartListField(*field, *type); } ProtoWriter* ProtoWriter::EndList() { @@ -518,98 +515,7 @@ ProtoWriter* ProtoWriter::RenderDataPiece(StringPiece name, return this; } - // Pushing a ProtoElement and then pop it off at the end for 2 purposes: - // error location reporting and required field accounting. - element_.reset(new ProtoElement(element_.release(), field, *type, false)); - - if (field->kind() == google::protobuf::Field_Kind_TYPE_UNKNOWN || - field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) { - InvalidValue(field->type_url().empty() - ? google::protobuf::Field_Kind_Name(field->kind()) - : field->type_url(), - data.ValueAsStringOrDefault("")); - element_.reset(element()->pop()); - return this; - } - - switch (field->kind()) { - case google::protobuf::Field_Kind_TYPE_INT32: { - status = WriteInt32(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_SFIXED32: { - status = WriteSFixed32(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_SINT32: { - status = WriteSInt32(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_FIXED32: { - status = WriteFixed32(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_UINT32: { - status = WriteUInt32(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_INT64: { - status = WriteInt64(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_SFIXED64: { - status = WriteSFixed64(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_SINT64: { - status = WriteSInt64(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_FIXED64: { - status = WriteFixed64(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_UINT64: { - status = WriteUInt64(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_DOUBLE: { - status = WriteDouble(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_FLOAT: { - status = WriteFloat(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_BOOL: { - status = WriteBool(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_BYTES: { - status = WriteBytes(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_STRING: { - status = WriteString(field->number(), data, stream_.get()); - break; - } - case google::protobuf::Field_Kind_TYPE_ENUM: { - status = WriteEnum(field->number(), data, - typeinfo_->GetEnumByTypeUrl(field->type_url()), - stream_.get()); - break; - } - default: // TYPE_GROUP or TYPE_MESSAGE - status = Status(INVALID_ARGUMENT, data.ToString().ValueOrDie()); - } - - if (!status.ok()) { - InvalidValue(google::protobuf::Field_Kind_Name(field->kind()), - status.error_message()); - } - - element_.reset(element()->pop()); - return this; + return RenderPrimitiveField(*field, *type, data); } bool ProtoWriter::ValidOneof(const google::protobuf::Field& field, @@ -635,6 +541,118 @@ bool ProtoWriter::IsRepeated(const google::protobuf::Field& field) { google::protobuf::Field_Cardinality_CARDINALITY_REPEATED; } +ProtoWriter* ProtoWriter::StartObjectField(const google::protobuf::Field& field, + const google::protobuf::Type& type) { + WriteTag(field); + element_.reset(new ProtoElement(element_.release(), &field, type, false)); + return this; +} + +ProtoWriter* ProtoWriter::StartListField(const google::protobuf::Field& field, + const google::protobuf::Type& type) { + element_.reset(new ProtoElement(element_.release(), &field, type, true)); + return this; +} + +ProtoWriter* ProtoWriter::RenderPrimitiveField( + const google::protobuf::Field& field, const google::protobuf::Type& type, + const DataPiece& data) { + Status status; + + // Pushing a ProtoElement and then pop it off at the end for 2 purposes: + // error location reporting and required field accounting. + element_.reset(new ProtoElement(element_.release(), &field, type, false)); + + if (field.kind() == google::protobuf::Field_Kind_TYPE_UNKNOWN || + field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) { + InvalidValue(field.type_url().empty() + ? google::protobuf::Field_Kind_Name(field.kind()) + : field.type_url(), + data.ValueAsStringOrDefault("")); + element_.reset(element()->pop()); + return this; + } + + switch (field.kind()) { + case google::protobuf::Field_Kind_TYPE_INT32: { + status = WriteInt32(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_SFIXED32: { + status = WriteSFixed32(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_SINT32: { + status = WriteSInt32(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_FIXED32: { + status = WriteFixed32(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_UINT32: { + status = WriteUInt32(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_INT64: { + status = WriteInt64(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_SFIXED64: { + status = WriteSFixed64(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_SINT64: { + status = WriteSInt64(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_FIXED64: { + status = WriteFixed64(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_UINT64: { + status = WriteUInt64(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_DOUBLE: { + status = WriteDouble(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_FLOAT: { + status = WriteFloat(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_BOOL: { + status = WriteBool(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_BYTES: { + status = WriteBytes(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_STRING: { + status = WriteString(field.number(), data, stream_.get()); + break; + } + case google::protobuf::Field_Kind_TYPE_ENUM: { + status = WriteEnum(field.number(), data, + typeinfo_->GetEnumByTypeUrl(field.type_url()), + stream_.get()); + break; + } + default: // TYPE_GROUP or TYPE_MESSAGE + status = Status(INVALID_ARGUMENT, data.ToString().ValueOrDie()); + } + + if (!status.ok()) { + InvalidValue(google::protobuf::Field_Kind_Name(field.kind()), + status.error_message()); + } + + element_.reset(element()->pop()); + return this; +} + const google::protobuf::Field* ProtoWriter::BeginNamed(StringPiece name, bool is_list) { if (invalid_depth_ > 0) { diff --git a/src/google/protobuf/util/internal/proto_writer.h b/src/google/protobuf/util/internal/proto_writer.h index e631e56f2..957565e74 100644 --- a/src/google/protobuf/util/internal/proto_writer.h +++ b/src/google/protobuf/util/internal/proto_writer.h @@ -106,10 +106,12 @@ class LIBPROTOBUF_EXPORT ProtoWriter : public StructuredObjectWriter { return RenderDataPiece(name, DataPiece(value)); } virtual ProtoWriter* RenderString(StringPiece name, StringPiece value) { - return RenderDataPiece(name, DataPiece(value)); + return RenderDataPiece(name, + DataPiece(value, use_strict_base64_decoding())); } virtual ProtoWriter* RenderBytes(StringPiece name, StringPiece value) { - return RenderDataPiece(name, DataPiece(value, false)); + return RenderDataPiece( + name, DataPiece(value, false, use_strict_base64_decoding())); } virtual ProtoWriter* RenderNull(StringPiece name) { return RenderDataPiece(name, DataPiece::NullData()); @@ -126,7 +128,7 @@ class LIBPROTOBUF_EXPORT ProtoWriter : public StructuredObjectWriter { } // When true, we finished writing to output a complete message. - bool done() const { return done_; } + bool done() { return done_; } // Returns the proto stream object. google::protobuf::io::CodedOutputStream* stream() { return stream_.get(); } @@ -266,6 +268,19 @@ class LIBPROTOBUF_EXPORT ProtoWriter : public StructuredObjectWriter { // Returns true if the field is repeated. bool IsRepeated(const google::protobuf::Field& field); + // Starts an object given the field and the enclosing type. + ProtoWriter* StartObjectField(const google::protobuf::Field& field, + const google::protobuf::Type& type); + + // Starts a list given the field and the enclosing type. + ProtoWriter* StartListField(const google::protobuf::Field& field, + const google::protobuf::Type& type); + + // Renders a primitve field given the field and the enclosing type. + ProtoWriter* RenderPrimitiveField(const google::protobuf::Field& field, + const google::protobuf::Type& type, + const DataPiece& value); + private: // Variables for describing the structure of the input tree: // master_type_: descriptor for the whole protobuf message. diff --git a/src/google/protobuf/util/internal/protostream_objectsource.cc b/src/google/protobuf/util/internal/protostream_objectsource.cc index 034d616ff..1f3781a4b 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource.cc +++ b/src/google/protobuf/util/internal/protostream_objectsource.cc @@ -70,6 +70,9 @@ using util::Status; using util::StatusOr; namespace { + +static int kDefaultMaxRecursionDepth = 64; + // Finds a field with the given number. NULL if none found. const google::protobuf::Field* FindFieldByNumber( const google::protobuf::Type& type, int number); @@ -83,6 +86,29 @@ const google::protobuf::EnumValue* FindEnumValueByNumber( // Utility function to format nanos. const string FormatNanos(uint32 nanos); + +StatusOr MapKeyDefaultValueAsString( + const google::protobuf::Field& field) { + switch (field.kind()) { + case google::protobuf::Field_Kind_TYPE_BOOL: + return string("false"); + case google::protobuf::Field_Kind_TYPE_INT32: + case google::protobuf::Field_Kind_TYPE_INT64: + case google::protobuf::Field_Kind_TYPE_UINT32: + case google::protobuf::Field_Kind_TYPE_UINT64: + case google::protobuf::Field_Kind_TYPE_SINT32: + case google::protobuf::Field_Kind_TYPE_SINT64: + case google::protobuf::Field_Kind_TYPE_SFIXED32: + case google::protobuf::Field_Kind_TYPE_SFIXED64: + case google::protobuf::Field_Kind_TYPE_FIXED32: + case google::protobuf::Field_Kind_TYPE_FIXED64: + return string("0"); + case google::protobuf::Field_Kind_TYPE_STRING: + return string(); + default: + return Status(util::error::INTERNAL, "Invalid map key type."); + } +} } // namespace @@ -92,14 +118,23 @@ ProtoStreamObjectSource::ProtoStreamObjectSource( : stream_(stream), typeinfo_(TypeInfo::NewTypeInfo(type_resolver)), own_typeinfo_(true), - type_(type) { + type_(type), + use_lower_camel_for_enums_(false), + recursion_depth_(0), + max_recursion_depth_(kDefaultMaxRecursionDepth) { GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL."; } ProtoStreamObjectSource::ProtoStreamObjectSource( google::protobuf::io::CodedInputStream* stream, const TypeInfo* typeinfo, const google::protobuf::Type& type) - : stream_(stream), typeinfo_(typeinfo), own_typeinfo_(false), type_(type) { + : stream_(stream), + typeinfo_(typeinfo), + own_typeinfo_(false), + type_(type), + use_lower_camel_for_enums_(false), + recursion_depth_(0), + max_recursion_depth_(kDefaultMaxRecursionDepth) { GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL."; } @@ -238,9 +273,21 @@ StatusOr ProtoStreamObjectSource::RenderMap( map_key = ReadFieldValueAsString(*field); } else if (field->number() == 2) { if (map_key.empty()) { - return Status(util::error::INTERNAL, "Map key must be non-empty"); + // An absent map key is treated as the default. + const google::protobuf::Field* key_field = + FindFieldByNumber(*field_type, 1); + if (key_field == NULL) { + // The Type info for this map entry is incorrect. It should always + // have a field named "key" and with field number 1. + return Status(util::error::INTERNAL, "Invalid map entry."); + } + ASSIGN_OR_RETURN(map_key, MapKeyDefaultValueAsString(*key_field)); } RETURN_IF_ERROR(RenderField(field, map_key, ow)); + } else { + // The Type info for this map entry is incorrect. It should contain + // exactly two fields with field number 1 and 2. + return Status(util::error::INTERNAL, "Invalid map entry."); } } stream_->PopLimit(old_limit); @@ -266,7 +313,7 @@ Status ProtoStreamObjectSource::RenderTimestamp( pair p = os->ReadSecondsAndNanos(type); int64 seconds = p.first; int32 nanos = p.second; - if (seconds > kMaxSeconds || seconds < kMinSeconds) { + if (seconds > kTimestampMaxSeconds || seconds < kTimestampMinSeconds) { return Status( util::error::INTERNAL, StrCat("Timestamp seconds exceeds limit for field: ", field_name)); @@ -290,7 +337,7 @@ Status ProtoStreamObjectSource::RenderDuration( pair p = os->ReadSecondsAndNanos(type); int64 seconds = p.first; int32 nanos = p.second; - if (seconds > kMaxSeconds || seconds < kMinSeconds) { + if (seconds > kDurationMaxSeconds || seconds < kDurationMinSeconds) { return Status( util::error::INTERNAL, StrCat("Duration seconds exceeds limit for field: ", field_name)); @@ -701,7 +748,9 @@ Status ProtoStreamObjectSource::RenderField( if (use_type_renderer) { RETURN_IF_ERROR((*type_renderer)(this, *type, field_name, ow)); } else { + RETURN_IF_ERROR(IncrementRecursionDepth(type->name(), field_name)); RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow)); + --recursion_depth_; } if (!stream_->ConsumedEntireMessage()) { return Status(util::error::INVALID_ARGUMENT, @@ -807,7 +856,10 @@ Status ProtoStreamObjectSource::RenderNonMessageField( const google::protobuf::EnumValue* enum_value = FindEnumValueByNumber(*en, buffer32); if (enum_value != NULL) { - ow->RenderString(field_name, enum_value->name()); + if (use_lower_camel_for_enums_) + ow->RenderString(field_name, ToCamelCase(enum_value->name())); + else + ow->RenderString(field_name, enum_value->name()); } } else { GOOGLE_LOG(INFO) << "Unknown enum skipped: " << field->type_url(); @@ -994,6 +1046,17 @@ std::pair ProtoStreamObjectSource::ReadSecondsAndNanos( return std::pair(signed_seconds, signed_nanos); } +Status ProtoStreamObjectSource::IncrementRecursionDepth( + StringPiece type_name, StringPiece field_name) const { + if (++recursion_depth_ > max_recursion_depth_) { + return Status( + util::error::INVALID_ARGUMENT, + StrCat("Message too deep. Max recursion depth reached for type '", + type_name, "', field '", field_name, "'")); + } + return Status::OK; +} + namespace { // TODO(skarvaje): Speed this up by not doing a linear scan. const google::protobuf::Field* FindFieldByNumber( diff --git a/src/google/protobuf/util/internal/protostream_objectsource.h b/src/google/protobuf/util/internal/protostream_objectsource.h index 78defa1d4..d7d4347b3 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource.h +++ b/src/google/protobuf/util/internal/protostream_objectsource.h @@ -82,6 +82,41 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { virtual util::Status NamedWriteTo(StringPiece name, ObjectWriter* ow) const; + // Sets whether or not to use lowerCamelCase casing for enum values. If set to + // false, enum values are output without any case conversions. + // + // For example, if we have an enum: + // enum Type { + // ACTION_AND_ADVENTURE = 1; + // } + // Type type = 20; + // + // And this option is set to true. Then the rendered "type" field will have + // the string "actionAndAdventure". + // { + // ... + // "type": "actionAndAdventure", + // ... + // } + // + // If set to false, the rendered "type" field will have the string + // "ACTION_AND_ADVENTURE". + // { + // ... + // "type": "ACTION_AND_ADVENTURE", + // ... + // } + void set_use_lower_camel_for_enums(bool value) { + use_lower_camel_for_enums_ = value; + } + + // Sets the max recursion depth of proto message to be deserialized. Proto + // messages over this depth will fail to be deserialized. + // Default value is 64. + void set_max_recursion_depth(int max_depth) { + max_recursion_depth_ = max_depth; + } + protected: // Writes a proto2 Message to the ObjectWriter. When the given end_tag is // found this method will complete, allowing it to be used for parsing both @@ -223,6 +258,12 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { std::pair ReadSecondsAndNanos( const google::protobuf::Type& type) const; + // Helper function to check recursion depth and increment it. It will return + // Status::OK if the current depth is allowed. Otherwise an error is returned. + // type_name and field_name are used for error reporting. + util::Status IncrementRecursionDepth(StringPiece type_name, + StringPiece field_name) const; + // Input stream to read from. Ownership rests with the caller. google::protobuf::io::CodedInputStream* stream_; @@ -237,6 +278,15 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { const google::protobuf::Type& type_; + // Whether to render enums using lowerCamelCase. Defaults to false. + bool use_lower_camel_for_enums_; + + // Tracks current recursion depth. + mutable int recursion_depth_; + + // Maximum allowed recursion depth. + int max_recursion_depth_; + GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectSource); }; diff --git a/src/google/protobuf/util/internal/protostream_objectsource_test.cc b/src/google/protobuf/util/internal/protostream_objectsource_test.cc index 561f67634..3f6fdf973 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource_test.cc +++ b/src/google/protobuf/util/internal/protostream_objectsource_test.cc @@ -50,6 +50,7 @@ #include #include #include +#include #include @@ -69,12 +70,15 @@ using google::protobuf::testing::Author; using google::protobuf::testing::BadAuthor; using google::protobuf::testing::BadNestedBook; using google::protobuf::testing::Book; +using google::protobuf::testing::Cyclic; using google::protobuf::testing::Book_Label; using google::protobuf::testing::NestedBook; using google::protobuf::testing::PackedPrimitive; using google::protobuf::testing::Primitive; using google::protobuf::testing::more_author; using google::protobuf::testing::maps::MapOut; +using google::protobuf::testing::maps::MapOutWireFormat; +using google::protobuf::testing::timestampduration::TimestampDuration; using google::protobuf::testing::anys::AnyOut; using google::protobuf::testing::anys::AnyM; using google::protobuf::testing::FieldMaskTest; @@ -92,7 +96,11 @@ string GetTypeUrl(const Descriptor* descriptor) { class ProtostreamObjectSourceTest : public ::testing::TestWithParam { protected: - ProtostreamObjectSourceTest() : helper_(GetParam()), mock_(), ow_(&mock_) { + ProtostreamObjectSourceTest() + : helper_(GetParam()), + mock_(), + ow_(&mock_), + use_lower_camel_for_enums_(false) { helper_.ResetTypeInfo(Book::descriptor()); } @@ -112,6 +120,8 @@ class ProtostreamObjectSourceTest google::protobuf::scoped_ptr os( helper_.NewProtoSource(&in_stream, GetTypeUrl(descriptor))); + if (use_lower_camel_for_enums_) os->set_use_lower_camel_for_enums(true); + os->set_max_recursion_depth(64); return os->WriteTo(&mock_); } @@ -256,10 +266,13 @@ class ProtostreamObjectSourceTest return primitive; } + void UseLowerCamelForEnums() { use_lower_camel_for_enums_ = true; } + testing::TypeInfoTestHelper helper_; ::testing::NiceMock mock_; ExpectingObjectWriter ow_; + bool use_lower_camel_for_enums_; }; INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, @@ -461,6 +474,52 @@ TEST_P(ProtostreamObjectSourceTest, DoTest(book, Book::descriptor()); } +TEST_P(ProtostreamObjectSourceTest, LowerCamelEnumOutputTest) { + Book book; + book.set_type(Book::ACTION_AND_ADVENTURE); + + UseLowerCamelForEnums(); + + ow_.StartObject("")->RenderString("type", "actionAndAdventure")->EndObject(); + DoTest(book, Book::descriptor()); +} + +TEST_P(ProtostreamObjectSourceTest, EnumCaseIsUnchangedByDefault) { + Book book; + book.set_type(Book::ACTION_AND_ADVENTURE); + ow_.StartObject("") + ->RenderString("type", "ACTION_AND_ADVENTURE") + ->EndObject(); + DoTest(book, Book::descriptor()); +} + +TEST_P(ProtostreamObjectSourceTest, CyclicMessageDepthTest) { + Cyclic cyclic; + cyclic.set_m_int(123); + + Book* book = cyclic.mutable_m_book(); + book->set_title("book title"); + Cyclic* current = cyclic.mutable_m_cyclic(); + Author* current_author = cyclic.add_m_author(); + for (int i = 0; i < 63; ++i) { + Author* next = current_author->add_friend_(); + next->set_id(i); + next->set_name(StrCat("author_name_", i)); + next->set_alive(true); + current_author = next; + } + + // Recursive message with depth (65) > max (max is 64). + for (int i = 0; i < 64; ++i) { + Cyclic* next = current->mutable_m_cyclic(); + next->set_m_str(StrCat("count_", i)); + current = next; + } + + Status status = ExecuteTest(cyclic, Cyclic::descriptor()); + EXPECT_EQ(util::error::INVALID_ARGUMENT, status.error_code()); +} + class ProtostreamObjectSourceMapsTest : public ProtostreamObjectSourceTest { protected: ProtostreamObjectSourceMapsTest() { @@ -541,6 +600,67 @@ TEST_P(ProtostreamObjectSourceMapsTest, MapsTest) { DoTest(out, MapOut::descriptor()); } +TEST_P(ProtostreamObjectSourceMapsTest, MissingKeysTest) { + // MapOutWireFormat has the same wire representation with MapOut but uses + // repeated message fields to represent map fields so we can intentionally + // leave out the key field or the value field of a map entry. + MapOutWireFormat out; + // Create some map entries without keys. They will be rendered with the + // default values ("" for strings, "0" for integers, etc.). + // { + // "map1": { + // "": { + // "foo": "foovalue" + // } + // }, + // "map2": { + // "": { + // "map1": { + // "nested_key1": { + // "foo": "nested_foo" + // } + // } + // } + // }, + // "map3": { + // "0": "one one one" + // }, + // "map4": { + // "false": "bool" + // } + // } + out.add_map1()->mutable_value()->set_foo("foovalue"); + MapOut* nested = out.add_map2()->mutable_value(); + (*nested->mutable_map1())["nested_key1"].set_foo("nested_foo"); + out.add_map3()->set_value("one one one"); + out.add_map4()->set_value("bool"); + + ow_.StartObject("") + ->StartObject("map1") + ->StartObject("") + ->RenderString("foo", "foovalue") + ->EndObject() + ->EndObject() + ->StartObject("map2") + ->StartObject("") + ->StartObject("map1") + ->StartObject("nested_key1") + ->RenderString("foo", "nested_foo") + ->EndObject() + ->EndObject() + ->EndObject() + ->EndObject() + ->StartObject("map3") + ->RenderString("0", "one one one") + ->EndObject() + ->StartObject("map4") + ->RenderString("false", "bool") + ->EndObject() + ->EndObject(); + + DoTest(out, MapOut::descriptor()); +} + class ProtostreamObjectSourceAnysTest : public ProtostreamObjectSourceTest { protected: ProtostreamObjectSourceAnysTest() { @@ -824,6 +944,63 @@ TEST_P(ProtostreamObjectSourceFieldMaskTest, FieldMaskRenderSuccess) { DoTest(out, FieldMaskTest::descriptor()); } +class ProtostreamObjectSourceTimestampTest + : public ProtostreamObjectSourceTest { + protected: + ProtostreamObjectSourceTimestampTest() { + helper_.ResetTypeInfo(TimestampDuration::descriptor()); + } +}; + +INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, + ProtostreamObjectSourceTimestampTest, + ::testing::Values( + testing::USE_TYPE_RESOLVER)); + +TEST_P(ProtostreamObjectSourceTimestampTest, InvalidTimestampBelowMinTest) { + TimestampDuration out; + google::protobuf::Timestamp* ts = out.mutable_ts(); + // Min allowed seconds - 1 + ts->set_seconds(kTimestampMinSeconds - 1); + ow_.StartObject(""); + + Status status = ExecuteTest(out, TimestampDuration::descriptor()); + EXPECT_EQ(util::error::INTERNAL, status.error_code()); +} + +TEST_P(ProtostreamObjectSourceTimestampTest, InvalidTimestampAboveMaxTest) { + TimestampDuration out; + google::protobuf::Timestamp* ts = out.mutable_ts(); + // Max allowed seconds + 1 + ts->set_seconds(kTimestampMaxSeconds + 1); + ow_.StartObject(""); + + Status status = ExecuteTest(out, TimestampDuration::descriptor()); + EXPECT_EQ(util::error::INTERNAL, status.error_code()); +} + +TEST_P(ProtostreamObjectSourceTimestampTest, InvalidDurationBelowMinTest) { + TimestampDuration out; + google::protobuf::Duration* dur = out.mutable_dur(); + // Min allowed seconds - 1 + dur->set_seconds(kDurationMinSeconds - 1); + ow_.StartObject(""); + + Status status = ExecuteTest(out, TimestampDuration::descriptor()); + EXPECT_EQ(util::error::INTERNAL, status.error_code()); +} + +TEST_P(ProtostreamObjectSourceTimestampTest, InvalidDurationAboveMaxTest) { + TimestampDuration out; + google::protobuf::Duration* dur = out.mutable_dur(); + // Min allowed seconds + 1 + dur->set_seconds(kDurationMaxSeconds + 1); + ow_.StartObject(""); + + Status status = ExecuteTest(out, TimestampDuration::descriptor()); + EXPECT_EQ(util::error::INTERNAL, status.error_code()); +} + } // namespace converter } // namespace util } // namespace protobuf diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.cc b/src/google/protobuf/util/internal/protostream_objectwriter.cc index 786bf0be2..97a7909a1 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter.cc +++ b/src/google/protobuf/util/internal/protostream_objectwriter.cc @@ -58,17 +58,20 @@ using util::StatusOr; ProtoStreamObjectWriter::ProtoStreamObjectWriter( TypeResolver* type_resolver, const google::protobuf::Type& type, - strings::ByteSink* output, ErrorListener* listener) + strings::ByteSink* output, ErrorListener* listener, + const ProtoStreamObjectWriter::Options& options) : ProtoWriter(type_resolver, type, output, listener), master_type_(type), - current_(NULL) {} + current_(NULL), + options_(options) {} ProtoStreamObjectWriter::ProtoStreamObjectWriter( const TypeInfo* typeinfo, const google::protobuf::Type& type, strings::ByteSink* output, ErrorListener* listener) : ProtoWriter(typeinfo, type, output, listener), master_type_(type), - current_(NULL) {} + current_(NULL), + options_(ProtoStreamObjectWriter::Options::Defaults()) {} ProtoStreamObjectWriter::~ProtoStreamObjectWriter() { if (current_ == NULL) return; @@ -179,7 +182,8 @@ ProtoStreamObjectWriter::AnyWriter::AnyWriter(ProtoStreamObjectWriter* parent) data_(), output_(&data_), depth_(0), - has_injected_value_message_(false) {} + is_well_known_type_(false), + well_known_type_render_(NULL) {} ProtoStreamObjectWriter::AnyWriter::~AnyWriter() {} @@ -197,10 +201,19 @@ void ProtoStreamObjectWriter::AnyWriter::StartObject(StringPiece name) { parent_->master_type_.name())); invalid_ = true; } - } else if (!has_injected_value_message_ || depth_ != 1 || name != "value") { - // We don't propagate to ow_ StartObject("value") calls for nested Anys or - // Struct at depth 1 as they are nested one level deep with an injected + } else if (is_well_known_type_ && depth_ == 1) { + // For well-known types, the only other field besides "@type" should be a // "value" field. + if (name != "value" && !invalid_) { + parent_->InvalidValue("Any", + "Expect a \"value\" field for well-known types."); + invalid_ = true; + } + ow_->StartObject(""); + } else { + // Forward the call to the child writer if: + // 1. the type is not a well-known type. + // 2. or, we are in a nested Any, Struct, or Value object. ow_->StartObject(name); } } @@ -208,10 +221,9 @@ void ProtoStreamObjectWriter::AnyWriter::StartObject(StringPiece name) { bool ProtoStreamObjectWriter::AnyWriter::EndObject() { --depth_; // As long as depth_ >= 0, we know we haven't reached the end of Any. - // Propagate these EndObject() calls to the contained ow_. If we are in a - // nested Any or Struct type, ignore the second to last EndObject call (depth_ - // == -1) - if (ow_ != NULL && (!has_injected_value_message_ || depth_ >= 0)) { + // Propagate these EndObject() calls to the contained ow_. For regular + // message types, we propagate the end of Any as well. + if (ow_ != NULL && (depth_ >= 0 || !is_well_known_type_)) { ow_->EndObject(); } // A negative depth_ implies that we have reached the end of Any @@ -233,6 +245,13 @@ void ProtoStreamObjectWriter::AnyWriter::StartList(StringPiece name) { parent_->master_type_.name())); invalid_ = true; } + } else if (is_well_known_type_ && depth_ == 1) { + if (name != "value" && !invalid_) { + parent_->InvalidValue("Any", + "Expect a \"value\" field for well-known types."); + invalid_ = true; + } + ow_->StartList(""); } else { ow_->StartList(name); } @@ -263,17 +282,27 @@ void ProtoStreamObjectWriter::AnyWriter::RenderDataPiece( parent_->master_type_.name())); invalid_ = true; } - } else { - // Check to see if the data needs to be rendered with well-known-type - // renderer. - const TypeRenderer* type_renderer = - FindTypeRenderer(GetFullTypeWithUrl(ow_->master_type_.name())); - if (type_renderer) { - Status status = (*type_renderer)(ow_.get(), value); - if (!status.ok()) ow_->InvalidValue("Any", status.error_message()); - } else { - ow_->RenderDataPiece(name, value); + } else if (depth_ == 0 && is_well_known_type_) { + if (name != "value" && !invalid_) { + parent_->InvalidValue("Any", + "Expect a \"value\" field for well-known types."); + invalid_ = true; } + if (well_known_type_render_ == NULL) { + // Only Any and Struct don't have a special type render but both of + // them expect a JSON object (i.e., a StartObject() call). + if (!invalid_) { + parent_->InvalidValue("Any", "Expect a JSON object."); + invalid_ = true; + } + } else { + ow_->ProtoWriter::StartObject(""); + Status status = (*well_known_type_render_)(ow_.get(), value); + if (!status.ok()) ow_->InvalidValue("Any", status.error_message()); + ow_->ProtoWriter::EndObject(); + } + } else { + ow_->RenderDataPiece(name, value); } } @@ -302,19 +331,31 @@ void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) { // At this point, type is never null. const google::protobuf::Type* type = resolved_type.ValueOrDie(); - // If this is the case of an Any in an Any or Struct in an Any, we need to - // expect a StartObject call with "value" while we're at depth_ 0, which we - // should ignore (not propagate to our nested object writer). We also need to - // ignore the second-to-last EndObject call, and not propagate that either. - if (type->name() == kAnyType || type->name() == kStructType) { - has_injected_value_message_ = true; + well_known_type_render_ = FindTypeRenderer(type_url_); + if (well_known_type_render_ != NULL || + // Explicitly list Any and Struct here because they don't have a + // custom renderer. + type->name() == kAnyType || type->name() == kStructType) { + is_well_known_type_ = true; } // Create our object writer and initialize it with the first StartObject // call. ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo(), *type, &output_, parent_->listener())); - ow_->StartObject(""); + + // Don't call StartObject() for well-known types yet. Depending on the + // type of actual data, we may not need to call StartObject(). For + // example: + // { + // "@type": "type.googleapis.com/google.protobuf.Value", + // "value": [1, 2, 3], + // } + // With the above JSON representation, we will only call StartList() on the + // contained ow_. + if (!is_well_known_type_) { + ow_->StartObject(""); + } } void ProtoStreamObjectWriter::AnyWriter::WriteAny() { @@ -439,7 +480,8 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject( // name): // { "key": "", "value": { Push("", Item::MESSAGE, false, false); - ProtoWriter::RenderDataPiece("key", DataPiece(name)); + ProtoWriter::RenderDataPiece("key", + DataPiece(name, use_strict_base64_decoding())); Push("value", Item::MESSAGE, true, false); // Make sure we are valid so far after starting map fields. @@ -604,7 +646,8 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) { // Render // { "key": "", "value": { Push("", Item::MESSAGE, false, false); - ProtoWriter::RenderDataPiece("key", DataPiece(name)); + ProtoWriter::RenderDataPiece("key", + DataPiece(name, use_strict_base64_decoding())); Push("value", Item::MESSAGE, true, false); // Make sure we are valid after pushing all above items. @@ -758,8 +801,36 @@ Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow, string struct_field_name; switch (data.type()) { // Our JSON parser parses numbers as either int64, uint64, or double. - case DataPiece::TYPE_INT64: - case DataPiece::TYPE_UINT64: + case DataPiece::TYPE_INT64: { + // If the option to treat integers as strings is set, then render them as + // strings. Otherwise, fallback to rendering them as double. + if (ow->options_.struct_integers_as_strings) { + StatusOr int_value = data.ToInt64(); + if (int_value.ok()) { + ow->ProtoWriter::RenderDataPiece( + "string_value", + DataPiece(SimpleItoa(int_value.ValueOrDie()), true)); + return Status::OK; + } + } + struct_field_name = "number_value"; + break; + } + case DataPiece::TYPE_UINT64: { + // If the option to treat integers as strings is set, then render them as + // strings. Otherwise, fallback to rendering them as double. + if (ow->options_.struct_integers_as_strings) { + StatusOr int_value = data.ToUint64(); + if (int_value.ok()) { + ow->ProtoWriter::RenderDataPiece( + "string_value", + DataPiece(SimpleItoa(int_value.ValueOrDie()), true)); + return Status::OK; + } + } + struct_field_name = "number_value"; + break; + } case DataPiece::TYPE_DOUBLE: { struct_field_name = "number_value"; break; @@ -812,7 +883,7 @@ Status ProtoStreamObjectWriter::RenderTimestamp(ProtoStreamObjectWriter* ow, static inline util::Status RenderOneFieldPath(ProtoStreamObjectWriter* ow, StringPiece path) { ow->ProtoWriter::RenderDataPiece( - "paths", DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase))); + "paths", DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase), true)); return Status::OK; } @@ -828,7 +899,7 @@ Status ProtoStreamObjectWriter::RenderFieldMask(ProtoStreamObjectWriter* ow, // conversions as much as possible. Because ToSnakeCase sometimes returns the // wrong value. google::protobuf::scoped_ptr > callback( - google::protobuf::internal::NewPermanentCallback(&RenderOneFieldPath, ow)); + ::google::protobuf::internal::NewPermanentCallback(&RenderOneFieldPath, ow)); return DecodeCompactFieldMaskPaths(data.str(), callback.get()); } @@ -871,7 +942,7 @@ Status ProtoStreamObjectWriter::RenderDuration(ProtoStreamObjectWriter* ow, nanos = sign * nanos; int64 seconds = sign * unsigned_seconds; - if (seconds > kMaxSeconds || seconds < kMinSeconds || + if (seconds > kDurationMaxSeconds || seconds < kDurationMinSeconds || nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) { return Status(INVALID_ARGUMENT, "Duration value exceeds limits"); } @@ -925,7 +996,8 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( // Render an item in repeated map list. // { "key": "", "value": Push("", Item::MESSAGE, false, false); - ProtoWriter::RenderDataPiece("key", DataPiece(name)); + ProtoWriter::RenderDataPiece("key", + DataPiece(name, use_strict_base64_decoding())); field = Lookup("value"); if (field == NULL) { GOOGLE_LOG(DFATAL) << "Map does not have a value field."; @@ -952,6 +1024,7 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( // not of the google.protobuf.NullType type, we do nothing. if (data.type() == DataPiece::TYPE_NULL && field->type_url() != kStructNullValueTypeUrl) { + Pop(); return this; } diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.h b/src/google/protobuf/util/internal/protostream_objectwriter.h index 08ac6e338..e1162d430 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter.h +++ b/src/google/protobuf/util/internal/protostream_objectwriter.h @@ -74,10 +74,30 @@ class ObjectLocationTracker; // It also supports streaming. class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public ProtoWriter { public: + // Options that control ProtoStreamObjectWriter class's behavior. + struct Options { + // Treats integer inputs in google.protobuf.Struct as strings. Normally, + // integer values are returned in double field "number_value" of + // google.protobuf.Struct. However, this can cause precision loss for + // int64/uint64 inputs. This option is provided for cases that want to + // preserve integer precision. + bool struct_integers_as_strings; + + Options() : struct_integers_as_strings(false) {} + + // Default instance of Options with all options set to defaults. + static const Options& Defaults() { + static Options defaults; + return defaults; + } + }; + // Constructor. Does not take ownership of any parameter passed in. ProtoStreamObjectWriter(TypeResolver* type_resolver, const google::protobuf::Type& type, - strings::ByteSink* output, ErrorListener* listener); + strings::ByteSink* output, ErrorListener* listener, + const ProtoStreamObjectWriter::Options& options = + ProtoStreamObjectWriter::Options::Defaults()); virtual ~ProtoStreamObjectWriter(); // ObjectWriter methods. @@ -147,9 +167,14 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public ProtoWriter { // The depth within the Any, so we can track when we're done. int depth_; - // True if the message type contained in Any has a special "value" message - // injected. This is true for well-known message types like Any or Struct. - bool has_injected_value_message_; + // True if the type is a well-known type. Well-known types in Any + // has a special formating: + // { + // "@type": "type.googleapis.com/google.protobuf.XXX", + // "value": , + // } + bool is_well_known_type_; + TypeRenderer* well_known_type_render_; }; // Represents an item in a stack of items used to keep state between @@ -301,6 +326,9 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public ProtoWriter { // The current element, variable for internal state processing. google::protobuf::scoped_ptr current_; + // Reference to the options that control this class's behavior. + const ProtoStreamObjectWriter::Options options_; + GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectWriter); }; diff --git a/src/google/protobuf/util/internal/protostream_objectwriter_test.cc b/src/google/protobuf/util/internal/protostream_objectwriter_test.cc index 5f9ffb957..9a0dcde1c 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter_test.cc +++ b/src/google/protobuf/util/internal/protostream_objectwriter_test.cc @@ -90,6 +90,12 @@ string GetTypeUrl(const Descriptor* descriptor) { } } // namespace +#if __cplusplus >= 201103L + using std::get; +#else + using std::tr1::get; +#endif + class BaseProtoStreamObjectWriterTest : public ::testing::TestWithParam { protected: @@ -122,7 +128,13 @@ class BaseProtoStreamObjectWriterTest GOOGLE_CHECK(!descriptors.empty()) << "Must have at least one descriptor!"; helper_.ResetTypeInfo(descriptors); ow_.reset(helper_.NewProtoWriter(GetTypeUrl(descriptors[0]), output_.get(), - &listener_)); + &listener_, options_)); + } + + void ResetTypeInfo(const Descriptor* descriptor) { + vector descriptors; + descriptors.push_back(descriptor); + ResetTypeInfo(descriptors); } virtual ~BaseProtoStreamObjectWriterTest() {} @@ -155,16 +167,12 @@ class BaseProtoStreamObjectWriterTest MockErrorListener listener_; google::protobuf::scoped_ptr output_; google::protobuf::scoped_ptr ow_; + ProtoStreamObjectWriter::Options options_; }; MATCHER_P(HasObjectLocation, expected, "Verifies the expected object location") { - string actual; -#if __cplusplus >= 201103L - actual = std::get<0>(arg).ToString(); -#else - actual = std::tr1::get<0>(arg).ToString(); -#endif + string actual = get<0>(arg).ToString(); if (actual.compare(expected) == 0) return true; *result_listener << "actual location is: " << actual; return false; @@ -289,8 +297,7 @@ TEST_P(ProtoStreamObjectWriterTest, PrimitiveFromStringConversion) { full.add_rep_double(-8.05L); full.add_rep_bool(false); - ow_.reset(helper_.NewProtoWriter(GetTypeUrl(Primitive::descriptor()), - output_.get(), &listener_)); + ResetTypeInfo(Primitive::descriptor()); ow_->StartObject("") ->RenderString("fix32", "101") @@ -363,8 +370,7 @@ TEST_P(ProtoStreamObjectWriterTest, InfinityInputTest) { full.set_float_(std::numeric_limits::infinity()); full.set_str("-Infinity"); - ow_.reset(helper_.NewProtoWriter(GetTypeUrl(Primitive::descriptor()), - output_.get(), &listener_)); + ResetTypeInfo(Primitive::descriptor()); EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_INT32"), StringPiece("\"Infinity\""))) @@ -397,8 +403,7 @@ TEST_P(ProtoStreamObjectWriterTest, NaNInputTest) { full.set_float_(std::numeric_limits::quiet_NaN()); full.set_str("NaN"); - ow_.reset(helper_.NewProtoWriter(GetTypeUrl(Primitive::descriptor()), - output_.get(), &listener_)); + ResetTypeInfo(Primitive::descriptor()); EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_INT32"), StringPiece("\"NaN\""))) @@ -887,6 +892,124 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, ParseTimestamp) { CheckOutput(timestamp); } +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, + ParseTimestampYearNotZeroPadded) { + TimestampDuration timestamp; + google::protobuf::Timestamp* ts = timestamp.mutable_ts(); + ts->set_seconds(-61665654145); + ts->set_nanos(33155000); + + ow_->StartObject("") + ->RenderString("ts", "15-11-23T03:37:35.033155Z") + ->EndObject(); + CheckOutput(timestamp); +} + +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, + ParseTimestampYearZeroPadded) { + TimestampDuration timestamp; + google::protobuf::Timestamp* ts = timestamp.mutable_ts(); + ts->set_seconds(-61665654145); + ts->set_nanos(33155000); + + ow_->StartObject("") + ->RenderString("ts", "0015-11-23T03:37:35.033155Z") + ->EndObject(); + CheckOutput(timestamp); +} + +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, + ParseTimestampWithPositiveOffset) { + TimestampDuration timestamp; + google::protobuf::Timestamp* ts = timestamp.mutable_ts(); + ts->set_seconds(1448249855); + ts->set_nanos(33155000); + + ow_->StartObject("") + ->RenderString("ts", "2015-11-23T11:47:35.033155+08:10") + ->EndObject(); + CheckOutput(timestamp); +} + +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, + ParseTimestampWithNegativeOffset) { + TimestampDuration timestamp; + google::protobuf::Timestamp* ts = timestamp.mutable_ts(); + ts->set_seconds(1448249855); + ts->set_nanos(33155000); + + ow_->StartObject("") + ->RenderString("ts", "2015-11-22T19:47:35.033155-07:50") + ->EndObject(); + CheckOutput(timestamp); +} + +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, + TimestampWithInvalidOffset1) { + TimestampDuration timestamp; + + EXPECT_CALL( + listener_, + InvalidValue(_, + StringPiece("type.googleapis.com/google.protobuf.Timestamp"), + StringPiece("Field 'ts', Invalid time format: " + "2016-03-07T15:14:23+"))); + + ow_->StartObject("")->RenderString("ts", "2016-03-07T15:14:23+")->EndObject(); + CheckOutput(timestamp); +} + +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, + TimestampWithInvalidOffset2) { + TimestampDuration timestamp; + + EXPECT_CALL( + listener_, + InvalidValue(_, + StringPiece("type.googleapis.com/google.protobuf.Timestamp"), + StringPiece("Field 'ts', Invalid time format: " + "2016-03-07T15:14:23+08-10"))); + + ow_->StartObject("") + ->RenderString("ts", "2016-03-07T15:14:23+08-10") + ->EndObject(); + CheckOutput(timestamp); +} + +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, + TimestampWithInvalidOffset3) { + TimestampDuration timestamp; + + EXPECT_CALL( + listener_, + InvalidValue(_, + StringPiece("type.googleapis.com/google.protobuf.Timestamp"), + StringPiece("Field 'ts', Invalid time format: " + "2016-03-07T15:14:23+24:10"))); + + ow_->StartObject("") + ->RenderString("ts", "2016-03-07T15:14:23+24:10") + ->EndObject(); + CheckOutput(timestamp); +} + +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, + TimestampWithInvalidOffset4) { + TimestampDuration timestamp; + + EXPECT_CALL( + listener_, + InvalidValue(_, + StringPiece("type.googleapis.com/google.protobuf.Timestamp"), + StringPiece("Field 'ts', Invalid time format: " + "2016-03-07T15:14:23+04:60"))); + + ow_->StartObject("") + ->RenderString("ts", "2016-03-07T15:14:23+04:60") + ->EndObject(); + CheckOutput(timestamp); +} + TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError1) { TimestampDuration timestamp; @@ -937,10 +1060,10 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError4) { InvalidValue(_, StringPiece("type.googleapis.com/google.protobuf.Timestamp"), StringPiece("Field 'ts', Invalid time format: " - "-8032-10-18T00:00:00.000Z"))); + "-8031-10-18T00:00:00.000Z"))); ow_->StartObject("") - ->RenderString("ts", "-8032-10-18T00:00:00.000Z") + ->RenderString("ts", "-8031-10-18T00:00:00.000Z") ->EndObject(); CheckOutput(timestamp); } @@ -996,6 +1119,22 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError7) { CheckOutput(timestamp); } +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError8) { + TimestampDuration timestamp; + + EXPECT_CALL( + listener_, + InvalidValue(_, + StringPiece("type.googleapis.com/google.protobuf.Timestamp"), + StringPiece("Field 'ts', Invalid time format: " + "0-12-31T23:59:59.000Z"))); + + ow_->StartObject("") + ->RenderString("ts", "0-12-31T23:59:59.000Z") + ->EndObject(); + CheckOutput(timestamp); +} + TEST_P(ProtoStreamObjectWriterTimestampDurationTest, ParseDuration) { TimestampDuration duration; google::protobuf::Duration* dur = duration.mutable_dur(); @@ -1105,7 +1244,10 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, class ProtoStreamObjectWriterStructTest : public BaseProtoStreamObjectWriterTest { protected: - ProtoStreamObjectWriterStructTest() { + ProtoStreamObjectWriterStructTest() { ResetProtoWriter(); } + + // Resets ProtoWriter with current set of options and other state. + void ResetProtoWriter() { vector descriptors; descriptors.push_back(StructType::descriptor()); descriptors.push_back(google::protobuf::Struct::descriptor()); @@ -1201,6 +1343,28 @@ TEST_P(ProtoStreamObjectWriterStructTest, RepeatedStructMapObjectKeyTest) { ->EndObject(); } +TEST_P(ProtoStreamObjectWriterStructTest, OptionStructIntAsStringsTest) { + StructType struct_type; + google::protobuf::Struct* s = struct_type.mutable_object(); + s->mutable_fields()->operator[]("k1").set_number_value(123); + s->mutable_fields()->operator[]("k2").set_bool_value(true); + s->mutable_fields()->operator[]("k3").set_string_value("-222222222"); + s->mutable_fields()->operator[]("k4").set_string_value("33333333"); + + options_.struct_integers_as_strings = true; + ResetProtoWriter(); + + ow_->StartObject("") + ->StartObject("object") + ->RenderDouble("k1", 123) + ->RenderBool("k2", true) + ->RenderInt64("k3", -222222222) + ->RenderUint64("k4", 33333333) + ->EndObject() + ->EndObject(); + CheckOutput(struct_type); +} + class ProtoStreamObjectWriterMapTest : public BaseProtoStreamObjectWriterTest { protected: ProtoStreamObjectWriterMapTest() @@ -1249,6 +1413,8 @@ class ProtoStreamObjectWriterAnyTest : public BaseProtoStreamObjectWriterTest { descriptors.push_back(google::protobuf::DoubleValue::descriptor()); descriptors.push_back(google::protobuf::Timestamp::descriptor()); descriptors.push_back(google::protobuf::Any::descriptor()); + descriptors.push_back(google::protobuf::Value::descriptor()); + descriptors.push_back(google::protobuf::Struct::descriptor()); ResetTypeInfo(descriptors); } }; @@ -1457,6 +1623,222 @@ TEST_P(ProtoStreamObjectWriterAnyTest, AnyWellKnownTypeErrorTest) { CheckOutput(any); } +// Test the following case: +// +// { +// "any": { +// "@type": "type.googleapis.com/google.protobuf.Value", +// "value": "abc" +// } +// } +TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithNestedPrimitiveValue) { + AnyOut out; + ::google::protobuf::Any* any = out.mutable_any(); + + ::google::protobuf::Value value; + value.set_string_value("abc"); + any->PackFrom(value); + + ow_->StartObject("") + ->StartObject("any") + ->RenderString("@type", "type.googleapis.com/google.protobuf.Value") + ->RenderString("value", "abc") + ->EndObject() + ->EndObject(); + CheckOutput(out); +} + +// Test the following case: +// +// { +// "any": { +// "@type": "type.googleapis.com/google.protobuf.Value", +// "value": { +// "foo": "abc" +// } +// } +// } +TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithNestedObjectValue) { + AnyOut out; + ::google::protobuf::Any* any = out.mutable_any(); + + ::google::protobuf::Value value; + (*value.mutable_struct_value()->mutable_fields())["foo"].set_string_value( + "abc"); + any->PackFrom(value); + + ow_->StartObject("") + ->StartObject("any") + ->RenderString("@type", "type.googleapis.com/google.protobuf.Value") + ->StartObject("value") + ->RenderString("foo", "abc") + ->EndObject() + ->EndObject() + ->EndObject(); + CheckOutput(out); +} + +// Test the following case: +// +// { +// "any": { +// "@type": "type.googleapis.com/google.protobuf.Value", +// "value": ["hello"], +// } +// } +TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithNestedArrayValue) { + AnyOut out; + ::google::protobuf::Any* any = out.mutable_any(); + + ::google::protobuf::Value value; + value.mutable_list_value()->add_values()->set_string_value("hello"); + any->PackFrom(value); + + ow_->StartObject("") + ->StartObject("any") + ->RenderString("@type", "type.googleapis.com/google.protobuf.Value") + ->StartList("value") + ->RenderString("", "hello") + ->EndList() + ->EndObject() + ->EndObject() + ->EndObject(); + CheckOutput(out); +} + +// Test the following case: +// +// { +// "any": { +// "@type": "type.googleapis.com/google.protobuf.Value", +// "not_value": "" +// } +// } +TEST_P(ProtoStreamObjectWriterAnyTest, + AnyWellKnownTypesNoValueFieldForPrimitive) { + EXPECT_CALL( + listener_, + InvalidValue( + _, StringPiece("Any"), + StringPiece("Expect a \"value\" field for well-known types."))); + AnyOut any; + google::protobuf::Any* any_type = any.mutable_any(); + any_type->set_type_url("type.googleapis.com/google.protobuf.Value"); + + ow_->StartObject("") + ->StartObject("any") + ->RenderString("@type", "type.googleapis.com/google.protobuf.Value") + ->RenderString("not_value", "") + ->EndObject() + ->EndObject(); + CheckOutput(any); +} + +// Test the following case: +// +// { +// "any": { +// "@type": "type.googleapis.com/google.protobuf.Value", +// "not_value": {} +// } +// } +TEST_P(ProtoStreamObjectWriterAnyTest, AnyWellKnownTypesNoValueFieldForObject) { + EXPECT_CALL( + listener_, + InvalidValue( + _, StringPiece("Any"), + StringPiece("Expect a \"value\" field for well-known types."))); + AnyOut any; + google::protobuf::Any* any_type = any.mutable_any(); + any_type->set_type_url("type.googleapis.com/google.protobuf.Value"); + + ow_->StartObject("") + ->StartObject("any") + ->RenderString("@type", "type.googleapis.com/google.protobuf.Value") + ->StartObject("not_value") + ->EndObject() + ->EndObject() + ->EndObject(); + CheckOutput(any); +} + +// Test the following case: +// +// { +// "any": { +// "@type": "type.googleapis.com/google.protobuf.Value", +// "not_value": [], +// } +// } +TEST_P(ProtoStreamObjectWriterAnyTest, AnyWellKnownTypesNoValueFieldForArray) { + EXPECT_CALL( + listener_, + InvalidValue( + _, StringPiece("Any"), + StringPiece("Expect a \"value\" field for well-known types."))); + AnyOut any; + google::protobuf::Any* any_type = any.mutable_any(); + any_type->set_type_url("type.googleapis.com/google.protobuf.Value"); + + ow_->StartObject("") + ->StartObject("any") + ->RenderString("@type", "type.googleapis.com/google.protobuf.Value") + ->StartList("not_value") + ->EndList() + ->EndObject() + ->EndObject() + ->EndObject(); + CheckOutput(any); +} + +// Test the following case: +// +// { +// "any": { +// "@type": "type.googleapis.com/google.protobuf.Struct", +// "value": "", +// } +// } +TEST_P(ProtoStreamObjectWriterAnyTest, AnyWellKnownTypesExpectObjectForStruct) { + EXPECT_CALL(listener_, InvalidValue(_, StringPiece("Any"), + StringPiece("Expect a JSON object."))); + AnyOut any; + google::protobuf::Any* any_type = any.mutable_any(); + any_type->set_type_url("type.googleapis.com/google.protobuf.Struct"); + + ow_->StartObject("") + ->StartObject("any") + ->RenderString("@type", "type.googleapis.com/google.protobuf.Struct") + ->RenderString("value", "") + ->EndObject() + ->EndObject(); + CheckOutput(any); +} + +// Test the following case: +// +// { +// "any": { +// "@type": "type.googleapis.com/google.protobuf.Any", +// "value": "", +// } +// } +TEST_P(ProtoStreamObjectWriterAnyTest, AnyWellKnownTypesExpectObjectForAny) { + EXPECT_CALL(listener_, InvalidValue(_, StringPiece("Any"), + StringPiece("Expect a JSON object."))); + AnyOut any; + google::protobuf::Any* any_type = any.mutable_any(); + any_type->set_type_url("type.googleapis.com/google.protobuf.Any"); + + ow_->StartObject("") + ->StartObject("any") + ->RenderString("@type", "type.googleapis.com/google.protobuf.Any") + ->RenderString("value", "") + ->EndObject() + ->EndObject(); + CheckOutput(any); +} + class ProtoStreamObjectWriterFieldMaskTest : public BaseProtoStreamObjectWriterTest { protected: diff --git a/src/google/protobuf/util/internal/testdata/books.proto b/src/google/protobuf/util/internal/testdata/books.proto index 82b817606..1cbbba478 100644 --- a/src/google/protobuf/util/internal/testdata/books.proto +++ b/src/google/protobuf/util/internal/testdata/books.proto @@ -56,6 +56,13 @@ message Book { optional Publisher publisher = 9; repeated Label labels = 10; + enum Type { + FICTION = 1; + KIDS = 2; + ACTION_AND_ADVENTURE = 3; + } + optional Type type = 11; + extensions 200 to 499; } @@ -169,3 +176,12 @@ message NestedBook { message BadNestedBook { repeated uint32 book = 1 [packed=true]; // Packed to optional message. } + +// A recursively defined message. +message Cyclic { + optional int32 m_int = 1; + optional string m_str = 2; + optional Book m_book = 3; + repeated Author m_author = 5; + optional Cyclic m_cyclic = 4; +} diff --git a/src/google/protobuf/util/internal/type_info_test_helper.cc b/src/google/protobuf/util/internal/type_info_test_helper.cc index 1b9c5154e..49e18ed0f 100644 --- a/src/google/protobuf/util/internal/type_info_test_helper.cc +++ b/src/google/protobuf/util/internal/type_info_test_helper.cc @@ -102,13 +102,13 @@ ProtoStreamObjectSource* TypeInfoTestHelper::NewProtoSource( } ProtoStreamObjectWriter* TypeInfoTestHelper::NewProtoWriter( - const string& type_url, strings::ByteSink* output, - ErrorListener* listener) { + const string& type_url, strings::ByteSink* output, ErrorListener* listener, + const ProtoStreamObjectWriter::Options& options) { const google::protobuf::Type* type = typeinfo_->GetTypeByTypeUrl(type_url); switch (type_) { case USE_TYPE_RESOLVER: { return new ProtoStreamObjectWriter(type_resolver_.get(), *type, output, - listener); + listener, options); } } GOOGLE_LOG(FATAL) << "Can not reach here."; diff --git a/src/google/protobuf/util/internal/type_info_test_helper.h b/src/google/protobuf/util/internal/type_info_test_helper.h index 6916a73b3..1a279849a 100644 --- a/src/google/protobuf/util/internal/type_info_test_helper.h +++ b/src/google/protobuf/util/internal/type_info_test_helper.h @@ -39,8 +39,8 @@ #include #include -#include #include +#include #include #include #include @@ -77,9 +77,9 @@ class TypeInfoTestHelper { ProtoStreamObjectSource* NewProtoSource(io::CodedInputStream* coded_input, const string& type_url); - ProtoStreamObjectWriter* NewProtoWriter(const string& type_url, - strings::ByteSink* output, - ErrorListener* listener); + ProtoStreamObjectWriter* NewProtoWriter( + const string& type_url, strings::ByteSink* output, + ErrorListener* listener, const ProtoStreamObjectWriter::Options& options); DefaultValueObjectWriter* NewDefaultValueWriter(const string& type_url, ObjectWriter* writer); diff --git a/src/google/protobuf/util/internal/utility.cc b/src/google/protobuf/util/internal/utility.cc index 1ddf24877..ee7a51fc4 100644 --- a/src/google/protobuf/util/internal/utility.cc +++ b/src/google/protobuf/util/internal/utility.cc @@ -222,6 +222,7 @@ string ToCamelCase(const StringPiece input) { if (!result.empty() && is_cap && (!was_cap || (i + 1 < input.size() && ascii_islower(input[i + 1])))) { first_word = false; + result.push_back(input[i]); } else { result.push_back(ascii_tolower(input[i])); continue; @@ -231,9 +232,13 @@ string ToCamelCase(const StringPiece input) { if (ascii_islower(input[i])) { result.push_back(ascii_toupper(input[i])); continue; + } else { + result.push_back(input[i]); + continue; } + } else { + result.push_back(ascii_tolower(input[i])); } - result.push_back(input[i]); } return result; } diff --git a/src/google/protobuf/util/json_util.cc b/src/google/protobuf/util/json_util.cc index c3b8d502e..2659320a1 100644 --- a/src/google/protobuf/util/json_util.cc +++ b/src/google/protobuf/util/json_util.cc @@ -102,6 +102,42 @@ util::Status BinaryToJsonString(TypeResolver* resolver, options); } +namespace { +class StatusErrorListener : public converter::ErrorListener { + public: + StatusErrorListener() : status_(util::Status::OK) {} + virtual ~StatusErrorListener() {} + + util::Status GetStatus() { return status_; } + + virtual void InvalidName(const converter::LocationTrackerInterface& loc, + StringPiece unknown_name, StringPiece message) { + status_ = util::Status(util::error::INVALID_ARGUMENT, + loc.ToString() + ": " + message.ToString()); + } + + virtual void InvalidValue(const converter::LocationTrackerInterface& loc, + StringPiece type_name, StringPiece value) { + status_ = + util::Status(util::error::INVALID_ARGUMENT, + loc.ToString() + ": invalid value " + value.ToString() + + " for type " + type_name.ToString()); + } + + virtual void MissingField(const converter::LocationTrackerInterface& loc, + StringPiece missing_name) { + status_ = util::Status( + util::error::INVALID_ARGUMENT, + loc.ToString() + ": missing field " + missing_name.ToString()); + } + + private: + util::Status status_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StatusErrorListener); +}; +} // namespace + util::Status JsonToBinaryStream(TypeResolver* resolver, const string& type_url, io::ZeroCopyInputStream* json_input, @@ -109,7 +145,7 @@ util::Status JsonToBinaryStream(TypeResolver* resolver, google::protobuf::Type type; RETURN_IF_ERROR(resolver->ResolveMessageType(type_url, &type)); internal::ZeroCopyStreamByteSink sink(binary_output); - converter::NoopErrorListener listener; + StatusErrorListener listener; converter::ProtoStreamObjectWriter proto_writer(resolver, type, &sink, &listener); @@ -123,7 +159,7 @@ util::Status JsonToBinaryStream(TypeResolver* resolver, } RETURN_IF_ERROR(parser.FinishParse()); - return util::Status::OK; + return listener.GetStatus(); } util::Status JsonToBinaryString(TypeResolver* resolver, diff --git a/src/google/protobuf/util/json_util_test.cc b/src/google/protobuf/util/json_util_test.cc index da68495fe..a4d3cc983 100644 --- a/src/google/protobuf/util/json_util_test.cc +++ b/src/google/protobuf/util/json_util_test.cc @@ -77,8 +77,11 @@ class JsonUtilTest : public testing::Test { bool FromJson(const string& json, Message* message) { string binary; - GOOGLE_CHECK_OK(JsonToBinaryString( - resolver_.get(), GetTypeUrl(message->GetDescriptor()), json, &binary)); + if (!JsonToBinaryString(resolver_.get(), + GetTypeUrl(message->GetDescriptor()), json, &binary) + .ok()) { + return false; + } return message->ParseFromString(binary); } @@ -99,28 +102,36 @@ TEST_F(JsonUtilTest, TestWhitespaces) { ToJson(m, options)); } -// TODO(skarvaje): Uncomment after cl/96232915 is submitted. -// TEST_F(JsonUtilTest, TestDefaultValues) { - // TestMessage m; - // JsonOptions options; - // EXPECT_EQ("{}", ToJson(m, options)); - // options.always_print_primitive_fields = true; - // EXPECT_EQ( - // "{\"boolValue\":false," - // "\"int32Value\":0," - // "\"int64Value\":\"0\"," - // "\"uint32Value\":0," - // "\"uint64Value\":\"0\"," - // "\"floatValue\":0," - // "\"doubleValue\":0," - // "\"stringValue\":\"\"," - // "\"bytesValue\":\"\"," - // // TODO(xiaofeng): The default enum value should be FOO. I believe - // // this is a bug in DefaultValueObjectWriter. - // "\"enumValue\":null" - // "}", - // ToJson(m, options)); -// } +TEST_F(JsonUtilTest, TestDefaultValues) { + TestMessage m; + JsonOptions options; + EXPECT_EQ("{}", ToJson(m, options)); + options.always_print_primitive_fields = true; + EXPECT_EQ( + "{\"boolValue\":false," + "\"int32Value\":0," + "\"int64Value\":\"0\"," + "\"uint32Value\":0," + "\"uint64Value\":\"0\"," + "\"floatValue\":0," + "\"doubleValue\":0," + "\"stringValue\":\"\"," + "\"bytesValue\":\"\"," + "\"enumValue\":\"FOO\"," + "\"repeatedBoolValue\":[]," + "\"repeatedInt32Value\":[]," + "\"repeatedInt64Value\":[]," + "\"repeatedUint32Value\":[]," + "\"repeatedUint64Value\":[]," + "\"repeatedFloatValue\":[]," + "\"repeatedDoubleValue\":[]," + "\"repeatedStringValue\":[]," + "\"repeatedBytesValue\":[]," + "\"repeatedEnumValue\":[]," + "\"repeatedMessageValue\":[]" + "}", + ToJson(m, options)); +} TEST_F(JsonUtilTest, ParseMessage) { // Some random message but good enough to verify that the parsing warpper @@ -158,6 +169,15 @@ TEST_F(JsonUtilTest, ParseMap) { EXPECT_EQ(message.DebugString(), other.DebugString()); } +TEST_F(JsonUtilTest, TestParseErrors) { + TestMessage m; + JsonOptions options; + // Parsing should fail if the field name can not be recognized. + EXPECT_FALSE(FromJson("{\"unknownName\":0}", &m)); + // Parsing should fail if the value is invalid. + EXPECT_FALSE(FromJson("{\"int32Value\":2147483648}", &m)); +} + typedef pair Segment; // A ZeroCopyOutputStream that writes to multiple buffers. class SegmentedZeroCopyOutputStream : public io::ZeroCopyOutputStream { diff --git a/src/google/protobuf/util/message_differencer.cc b/src/google/protobuf/util/message_differencer.cc index 0f879dc72..fe8119bf3 100644 --- a/src/google/protobuf/util/message_differencer.cc +++ b/src/google/protobuf/util/message_differencer.cc @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -455,7 +456,9 @@ bool MessageDifferencer::Compare( const Descriptor* descriptor2 = message2.GetDescriptor(); if (descriptor1 != descriptor2) { GOOGLE_LOG(DFATAL) << "Comparison between two messages with different " - << "descriptors."; + << "descriptors. " + << descriptor1->full_name() << " vs " + << descriptor2->full_name(); return false; } // Expand google.protobuf.Any payload if possible. @@ -1021,7 +1024,7 @@ bool MessageDifferencer::UnpackAny(const Message& any, any.GetDescriptor()->file()->pool()->FindMessageTypeByName( full_type_name); if (desc == NULL) { - GOOGLE_LOG(ERROR) << "Proto type '" << full_type_name << "' not found"; + GOOGLE_DLOG(ERROR) << "Proto type '" << full_type_name << "' not found"; return false; } @@ -1031,7 +1034,7 @@ bool MessageDifferencer::UnpackAny(const Message& any, data->reset(dynamic_message_factory_->GetPrototype(desc)->New()); string serialized_value = reflection->GetString(any, value_field); if (!(*data)->ParseFromString(serialized_value)) { - GOOGLE_LOG(ERROR) << "Failed to parse value for " << full_type_name; + GOOGLE_DLOG(ERROR) << "Failed to parse value for " << full_type_name; return false; } return true; @@ -1384,7 +1387,7 @@ bool MessageDifferencer::MatchRepeatedFieldIndices( // algorithm will fail to find a maximum matching. // Here we use the argumenting path algorithm. MaximumMatcher::NodeMatchCallback* callback = - google::protobuf::internal::NewPermanentCallback( + ::google::protobuf::internal::NewPermanentCallback( this, &MessageDifferencer::IsMatch, repeated_field, key_comparator, &message1, &message2, parent_fields); diff --git a/src/google/protobuf/wire_format_lite.cc b/src/google/protobuf/wire_format_lite.cc index 7f1093c8f..f25170746 100644 --- a/src/google/protobuf/wire_format_lite.cc +++ b/src/google/protobuf/wire_format_lite.cc @@ -412,7 +412,7 @@ void WireFormatLite::WriteString(int field_number, const string& value, io::CodedOutputStream* output) { // String is for UTF-8 text only WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output); - GOOGLE_CHECK(value.size() <= kint32max); + GOOGLE_CHECK_LE(value.size(), kint32max); output->WriteVarint32(value.size()); output->WriteString(value); } @@ -421,14 +421,14 @@ void WireFormatLite::WriteStringMaybeAliased( io::CodedOutputStream* output) { // String is for UTF-8 text only WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output); - GOOGLE_CHECK(value.size() <= kint32max); + GOOGLE_CHECK_LE(value.size(), kint32max); output->WriteVarint32(value.size()); output->WriteRawMaybeAliased(value.data(), value.size()); } void WireFormatLite::WriteBytes(int field_number, const string& value, io::CodedOutputStream* output) { WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output); - GOOGLE_CHECK(value.size() <= kint32max); + GOOGLE_CHECK_LE(value.size(), kint32max); output->WriteVarint32(value.size()); output->WriteString(value); } @@ -436,7 +436,7 @@ void WireFormatLite::WriteBytesMaybeAliased( int field_number, const string& value, io::CodedOutputStream* output) { WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output); - GOOGLE_CHECK(value.size() <= kint32max); + GOOGLE_CHECK_LE(value.size(), kint32max); output->WriteVarint32(value.size()); output->WriteRawMaybeAliased(value.data(), value.size()); } diff --git a/src/google/protobuf/wire_format_lite_inl.h b/src/google/protobuf/wire_format_lite_inl.h index 79493ca01..7bce21cf0 100644 --- a/src/google/protobuf/wire_format_lite_inl.h +++ b/src/google/protobuf/wire_format_lite_inl.h @@ -274,8 +274,8 @@ inline bool WireFormatLite::ReadRepeatedFixedSizePrimitive( // The number of bytes each type occupies on the wire. const int per_value_size = tag_size + sizeof(value); - int elements_available = min(values->Capacity() - values->size(), - size / per_value_size); + int elements_available = + std::min(values->Capacity() - values->size(), size / per_value_size); int num_read = 0; while (num_read < elements_available && (buffer = io::CodedInputStream::ExpectTagFromArray( @@ -367,7 +367,7 @@ inline bool WireFormatLite::ReadPackedFixedSizePrimitive( bytes_limit = input->BytesUntilLimit(); } else { bytes_limit = - min(bytes_limit, static_cast(input->BytesUntilLimit())); + std::min(bytes_limit, static_cast(input->BytesUntilLimit())); } if (bytes_limit >= new_bytes) { // Fast-path that pre-allocates *values to the final size. diff --git a/src/google/protobuf/wire_format_unittest.cc b/src/google/protobuf/wire_format_unittest.cc index 15c37556e..4e4add66f 100644 --- a/src/google/protobuf/wire_format_unittest.cc +++ b/src/google/protobuf/wire_format_unittest.cc @@ -45,6 +45,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/wrappers.pb.cc b/src/google/protobuf/wrappers.pb.cc index 212dd2191..608014232 100644 --- a/src/google/protobuf/wrappers.pb.cc +++ b/src/google/protobuf/wrappers.pb.cc @@ -263,10 +263,11 @@ void protobuf_AddDesc_google_2fprotobuf_2fwrappers_2eproto() { "e\030\001 \001(\004\"\033\n\nInt32Value\022\r\n\005value\030\001 \001(\005\"\034\n\013" "UInt32Value\022\r\n\005value\030\001 \001(\r\"\032\n\tBoolValue\022" "\r\n\005value\030\001 \001(\010\"\034\n\013StringValue\022\r\n\005value\030\001" - " \001(\t\"\033\n\nBytesValue\022\r\n\005value\030\001 \001(\014BS\n\023com" - ".google.protobufB\rWrappersProtoP\001\240\001\001\370\001\001\242" - "\002\003GPB\252\002\036Google.Protobuf.WellKnownTypesb\006" - "proto3", 406); + " \001(\t\"\033\n\nBytesValue\022\r\n\005value\030\001 \001(\014B\177\n\023com" + ".google.protobufB\rWrappersProtoP\001Z*githu" + "b.com/golang/protobuf/ptypes/wrappers\240\001\001" + "\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTyp" + "esb\006proto3", 450); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/wrappers.proto", &protobuf_RegisterTypes); DoubleValue::default_instance_ = new DoubleValue(); @@ -387,12 +388,13 @@ DoubleValue* DoubleValue::New(::google::protobuf::Arena* arena) const { } void DoubleValue::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.DoubleValue) value_ = 0; } bool DoubleValue::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.DoubleValue) for (;;) { @@ -459,6 +461,7 @@ void DoubleValue::SerializeWithCachedSizes( } int DoubleValue::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.DoubleValue) int total_size = 0; // optional double value = 1; @@ -473,18 +476,22 @@ int DoubleValue::ByteSize() const { } void DoubleValue::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.DoubleValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const DoubleValue* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.DoubleValue) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.DoubleValue) MergeFrom(*source); } } void DoubleValue::MergeFrom(const DoubleValue& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.DoubleValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.value() != 0) { set_value(from.value()); @@ -492,12 +499,14 @@ void DoubleValue::MergeFrom(const DoubleValue& from) { } void DoubleValue::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.DoubleValue) if (&from == this) return; Clear(); MergeFrom(from); } void DoubleValue::CopyFrom(const DoubleValue& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.DoubleValue) if (&from == this) return; Clear(); MergeFrom(from); @@ -637,12 +646,13 @@ FloatValue* FloatValue::New(::google::protobuf::Arena* arena) const { } void FloatValue::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.FloatValue) value_ = 0; } bool FloatValue::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.FloatValue) for (;;) { @@ -709,6 +719,7 @@ void FloatValue::SerializeWithCachedSizes( } int FloatValue::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.FloatValue) int total_size = 0; // optional float value = 1; @@ -723,18 +734,22 @@ int FloatValue::ByteSize() const { } void FloatValue::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.FloatValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const FloatValue* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.FloatValue) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.FloatValue) MergeFrom(*source); } } void FloatValue::MergeFrom(const FloatValue& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.FloatValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.value() != 0) { set_value(from.value()); @@ -742,12 +757,14 @@ void FloatValue::MergeFrom(const FloatValue& from) { } void FloatValue::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.FloatValue) if (&from == this) return; Clear(); MergeFrom(from); } void FloatValue::CopyFrom(const FloatValue& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.FloatValue) if (&from == this) return; Clear(); MergeFrom(from); @@ -887,12 +904,13 @@ Int64Value* Int64Value::New(::google::protobuf::Arena* arena) const { } void Int64Value::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Int64Value) value_ = GOOGLE_LONGLONG(0); } bool Int64Value::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Int64Value) for (;;) { @@ -959,6 +977,7 @@ void Int64Value::SerializeWithCachedSizes( } int Int64Value::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Int64Value) int total_size = 0; // optional int64 value = 1; @@ -975,18 +994,22 @@ int Int64Value::ByteSize() const { } void Int64Value::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Int64Value) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Int64Value* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Int64Value) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Int64Value) MergeFrom(*source); } } void Int64Value::MergeFrom(const Int64Value& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Int64Value) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.value() != 0) { set_value(from.value()); @@ -994,12 +1017,14 @@ void Int64Value::MergeFrom(const Int64Value& from) { } void Int64Value::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Int64Value) if (&from == this) return; Clear(); MergeFrom(from); } void Int64Value::CopyFrom(const Int64Value& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Int64Value) if (&from == this) return; Clear(); MergeFrom(from); @@ -1139,12 +1164,13 @@ UInt64Value* UInt64Value::New(::google::protobuf::Arena* arena) const { } void UInt64Value::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.UInt64Value) value_ = GOOGLE_ULONGLONG(0); } bool UInt64Value::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.UInt64Value) for (;;) { @@ -1211,6 +1237,7 @@ void UInt64Value::SerializeWithCachedSizes( } int UInt64Value::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.UInt64Value) int total_size = 0; // optional uint64 value = 1; @@ -1227,18 +1254,22 @@ int UInt64Value::ByteSize() const { } void UInt64Value::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.UInt64Value) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const UInt64Value* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.UInt64Value) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.UInt64Value) MergeFrom(*source); } } void UInt64Value::MergeFrom(const UInt64Value& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.UInt64Value) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.value() != 0) { set_value(from.value()); @@ -1246,12 +1277,14 @@ void UInt64Value::MergeFrom(const UInt64Value& from) { } void UInt64Value::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.UInt64Value) if (&from == this) return; Clear(); MergeFrom(from); } void UInt64Value::CopyFrom(const UInt64Value& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.UInt64Value) if (&from == this) return; Clear(); MergeFrom(from); @@ -1391,12 +1424,13 @@ Int32Value* Int32Value::New(::google::protobuf::Arena* arena) const { } void Int32Value::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.Int32Value) value_ = 0; } bool Int32Value::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.Int32Value) for (;;) { @@ -1463,6 +1497,7 @@ void Int32Value::SerializeWithCachedSizes( } int Int32Value::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Int32Value) int total_size = 0; // optional int32 value = 1; @@ -1479,18 +1514,22 @@ int Int32Value::ByteSize() const { } void Int32Value::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.Int32Value) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const Int32Value* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.Int32Value) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.Int32Value) MergeFrom(*source); } } void Int32Value::MergeFrom(const Int32Value& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Int32Value) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.value() != 0) { set_value(from.value()); @@ -1498,12 +1537,14 @@ void Int32Value::MergeFrom(const Int32Value& from) { } void Int32Value::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.Int32Value) if (&from == this) return; Clear(); MergeFrom(from); } void Int32Value::CopyFrom(const Int32Value& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Int32Value) if (&from == this) return; Clear(); MergeFrom(from); @@ -1643,12 +1684,13 @@ UInt32Value* UInt32Value::New(::google::protobuf::Arena* arena) const { } void UInt32Value::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.UInt32Value) value_ = 0u; } bool UInt32Value::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.UInt32Value) for (;;) { @@ -1715,6 +1757,7 @@ void UInt32Value::SerializeWithCachedSizes( } int UInt32Value::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.UInt32Value) int total_size = 0; // optional uint32 value = 1; @@ -1731,18 +1774,22 @@ int UInt32Value::ByteSize() const { } void UInt32Value::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.UInt32Value) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const UInt32Value* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.UInt32Value) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.UInt32Value) MergeFrom(*source); } } void UInt32Value::MergeFrom(const UInt32Value& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.UInt32Value) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.value() != 0) { set_value(from.value()); @@ -1750,12 +1797,14 @@ void UInt32Value::MergeFrom(const UInt32Value& from) { } void UInt32Value::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.UInt32Value) if (&from == this) return; Clear(); MergeFrom(from); } void UInt32Value::CopyFrom(const UInt32Value& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.UInt32Value) if (&from == this) return; Clear(); MergeFrom(from); @@ -1895,12 +1944,13 @@ BoolValue* BoolValue::New(::google::protobuf::Arena* arena) const { } void BoolValue::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.BoolValue) value_ = false; } bool BoolValue::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.BoolValue) for (;;) { @@ -1967,6 +2017,7 @@ void BoolValue::SerializeWithCachedSizes( } int BoolValue::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.BoolValue) int total_size = 0; // optional bool value = 1; @@ -1981,18 +2032,22 @@ int BoolValue::ByteSize() const { } void BoolValue::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.BoolValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const BoolValue* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.BoolValue) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.BoolValue) MergeFrom(*source); } } void BoolValue::MergeFrom(const BoolValue& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.BoolValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.value() != 0) { set_value(from.value()); @@ -2000,12 +2055,14 @@ void BoolValue::MergeFrom(const BoolValue& from) { } void BoolValue::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.BoolValue) if (&from == this) return; Clear(); MergeFrom(from); } void BoolValue::CopyFrom(const BoolValue& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.BoolValue) if (&from == this) return; Clear(); MergeFrom(from); @@ -2147,12 +2204,13 @@ StringValue* StringValue::New(::google::protobuf::Arena* arena) const { } void StringValue::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.StringValue) value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } bool StringValue::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.StringValue) for (;;) { @@ -2232,6 +2290,7 @@ void StringValue::SerializeWithCachedSizes( } int StringValue::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.StringValue) int total_size = 0; // optional string value = 1; @@ -2248,18 +2307,22 @@ int StringValue::ByteSize() const { } void StringValue::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.StringValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const StringValue* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.StringValue) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.StringValue) MergeFrom(*source); } } void StringValue::MergeFrom(const StringValue& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.StringValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.value().size() > 0) { set_value(from.value()); @@ -2267,12 +2330,14 @@ void StringValue::MergeFrom(const StringValue& from) { } void StringValue::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.StringValue) if (&from == this) return; Clear(); MergeFrom(from); } void StringValue::CopyFrom(const StringValue& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.StringValue) if (&from == this) return; Clear(); MergeFrom(from); @@ -2348,10 +2413,12 @@ void StringValue::clear_value() { return value_.Mutable(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } ::std::string* StringValue::release_value() { + // @@protoc_insertion_point(field_release:google.protobuf.StringValue.value) return value_.Release(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } ::std::string* StringValue::unsafe_arena_release_value() { + // @@protoc_insertion_point(field_unsafe_arena_release:google.protobuf.StringValue.value) GOOGLE_DCHECK(GetArenaNoVirtual() != NULL); return value_.UnsafeArenaRelease(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), @@ -2377,7 +2444,7 @@ void StringValue::clear_value() { } value_.UnsafeArenaSetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, GetArenaNoVirtual()); - // @@protoc_insertion_point(field_set_allocated:google.protobuf.StringValue.value) + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.StringValue.value) } #endif // PROTOBUF_INLINE_NOT_IN_HEADERS @@ -2464,12 +2531,13 @@ BytesValue* BytesValue::New(::google::protobuf::Arena* arena) const { } void BytesValue::Clear() { +// @@protoc_insertion_point(message_clear_start:google.protobuf.BytesValue) value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } bool BytesValue::MergePartialFromCodedStream( ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure ::google::protobuf::uint32 tag; // @@protoc_insertion_point(parse_start:google.protobuf.BytesValue) for (;;) { @@ -2537,6 +2605,7 @@ void BytesValue::SerializeWithCachedSizes( } int BytesValue::ByteSize() const { +// @@protoc_insertion_point(message_byte_size_start:google.protobuf.BytesValue) int total_size = 0; // optional bytes value = 1; @@ -2553,18 +2622,22 @@ int BytesValue::ByteSize() const { } void BytesValue::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:google.protobuf.BytesValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); const BytesValue* source = ::google::protobuf::internal::DynamicCastToGenerated( &from); if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:google.protobuf.BytesValue) ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:google.protobuf.BytesValue) MergeFrom(*source); } } void BytesValue::MergeFrom(const BytesValue& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.BytesValue) if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.value().size() > 0) { set_value(from.value()); @@ -2572,12 +2645,14 @@ void BytesValue::MergeFrom(const BytesValue& from) { } void BytesValue::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:google.protobuf.BytesValue) if (&from == this) return; Clear(); MergeFrom(from); } void BytesValue::CopyFrom(const BytesValue& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.BytesValue) if (&from == this) return; Clear(); MergeFrom(from); @@ -2653,10 +2728,12 @@ void BytesValue::clear_value() { return value_.Mutable(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } ::std::string* BytesValue::release_value() { + // @@protoc_insertion_point(field_release:google.protobuf.BytesValue.value) return value_.Release(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } ::std::string* BytesValue::unsafe_arena_release_value() { + // @@protoc_insertion_point(field_unsafe_arena_release:google.protobuf.BytesValue.value) GOOGLE_DCHECK(GetArenaNoVirtual() != NULL); return value_.UnsafeArenaRelease(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), @@ -2682,7 +2759,7 @@ void BytesValue::clear_value() { } value_.UnsafeArenaSetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, GetArenaNoVirtual()); - // @@protoc_insertion_point(field_set_allocated:google.protobuf.BytesValue.value) + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.BytesValue.value) } #endif // PROTOBUF_INLINE_NOT_IN_HEADERS diff --git a/src/google/protobuf/wrappers.pb.h b/src/google/protobuf/wrappers.pb.h index 7dca938c7..107847788 100644 --- a/src/google/protobuf/wrappers.pb.h +++ b/src/google/protobuf/wrappers.pb.h @@ -1048,10 +1048,12 @@ inline ::std::string* StringValue::mutable_value() { return value_.Mutable(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } inline ::std::string* StringValue::release_value() { + // @@protoc_insertion_point(field_release:google.protobuf.StringValue.value) return value_.Release(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } inline ::std::string* StringValue::unsafe_arena_release_value() { + // @@protoc_insertion_point(field_unsafe_arena_release:google.protobuf.StringValue.value) GOOGLE_DCHECK(GetArenaNoVirtual() != NULL); return value_.UnsafeArenaRelease(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), @@ -1077,7 +1079,7 @@ inline void StringValue::unsafe_arena_set_allocated_value( } value_.UnsafeArenaSetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, GetArenaNoVirtual()); - // @@protoc_insertion_point(field_set_allocated:google.protobuf.StringValue.value) + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.StringValue.value) } // ------------------------------------------------------------------- @@ -1116,10 +1118,12 @@ inline ::std::string* BytesValue::mutable_value() { return value_.Mutable(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } inline ::std::string* BytesValue::release_value() { + // @@protoc_insertion_point(field_release:google.protobuf.BytesValue.value) return value_.Release(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } inline ::std::string* BytesValue::unsafe_arena_release_value() { + // @@protoc_insertion_point(field_unsafe_arena_release:google.protobuf.BytesValue.value) GOOGLE_DCHECK(GetArenaNoVirtual() != NULL); return value_.UnsafeArenaRelease(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), @@ -1145,7 +1149,7 @@ inline void BytesValue::unsafe_arena_set_allocated_value( } value_.UnsafeArenaSetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, GetArenaNoVirtual()); - // @@protoc_insertion_point(field_set_allocated:google.protobuf.BytesValue.value) + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:google.protobuf.BytesValue.value) } #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS diff --git a/src/google/protobuf/wrappers.proto b/src/google/protobuf/wrappers.proto index 040d8a247..4828ad9ae 100644 --- a/src/google/protobuf/wrappers.proto +++ b/src/google/protobuf/wrappers.proto @@ -39,6 +39,7 @@ package google.protobuf; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; option cc_enable_arenas = true; +option go_package = "github.com/golang/protobuf/ptypes/wrappers"; option java_package = "com.google.protobuf"; option java_outer_classname = "WrappersProto"; option java_multiple_files = true; diff --git a/travis.sh b/tests.sh similarity index 73% rename from travis.sh rename to tests.sh index ff5e99d58..6a9439a55 100755 --- a/travis.sh +++ b/tests.sh @@ -1,17 +1,24 @@ -#!/usr/bin/env bash +#!/bin/bash +# +# Build and runs tests for the protobuf project. The tests as written here are +# used by both Jenkins and Travis, though some specialized logic is required to +# handle the differences between them. -# Note: travis currently does not support testing more than one language so the -# .travis.yml cheats and claims to only be cpp. If they add multiple language -# support, this should probably get updated to install steps and/or -# rvm/gemfile/jdk/etc. entries rather than manually doing the work. - -# .travis.yml uses matrix.exclude to block the cases where app-get can't be -# use to install things. +on_travis() { + if [ "$TRAVIS" == "true" ]; then + "$@" + fi +} # For when some other test needs the C++ main build, including protoc and # libprotobuf. internal_build_cpp() { - if [ $(uname -s) == "Linux" ]; then + if [ -f src/protoc ]; then + # Already built. + return + fi + + if [[ $(uname -s) == "Linux" && "$TRAVIS" == "true" ]]; then # Install GCC 4.8 to replace the default GCC 4.6. We need 4.8 for more # decent C++ 11 support in order to compile conformance tests. sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y @@ -29,6 +36,9 @@ build_cpp() { internal_build_cpp make check -j2 cd conformance && make test_cpp && cd .. + + # Verify benchmarking code can build successfully. + cd benchmarks && make && ./generate-datasets && cd .. } build_cpp_distcheck() { @@ -42,16 +52,20 @@ build_csharp() { # need to really build protoc, but it's simplest to keep with the # conventions of the other builds. internal_build_cpp + NUGET=/usr/local/bin/nuget.exe - # Install latest version of Mono - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF - echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list - echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list - sudo apt-get update -qq - sudo apt-get install -qq mono-devel referenceassemblies-pcl nunit - wget www.nuget.org/NuGet.exe -O nuget.exe + if [ "$TRAVIS" == "true" ]; then + # Install latest version of Mono + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF + echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list + echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list + sudo apt-get update -qq + sudo apt-get install -qq mono-devel referenceassemblies-pcl nunit + wget www.nuget.org/NuGet.exe -O nuget.exe + NUGET=../../nuget.exe + fi - (cd csharp/src; mono ../../nuget.exe restore) + (cd csharp/src; mono $NUGET restore) csharp/buildall.sh cd conformance && make test_csharp && cd .. } @@ -78,40 +92,54 @@ use_java() { version=$1 case "$version" in jdk6) - sudo apt-get install openjdk-6-jdk + on_travis sudo apt-get install openjdk-6-jdk export PATH=/usr/lib/jvm/java-6-openjdk-amd64/bin:$PATH ;; jdk7) - sudo apt-get install openjdk-7-jdk + on_travis sudo apt-get install openjdk-7-jdk export PATH=/usr/lib/jvm/java-7-openjdk-amd64/bin:$PATH ;; oracle7) - sudo apt-get install python-software-properties # for apt-add-repository - echo "oracle-java7-installer shared/accepted-oracle-license-v1-1 select true" | \ - sudo debconf-set-selections - yes | sudo apt-add-repository ppa:webupd8team/java - yes | sudo apt-get install oracle-java7-installer + if [ "$TRAVIS" == "true" ]; then + sudo apt-get install python-software-properties # for apt-add-repository + echo "oracle-java7-installer shared/accepted-oracle-license-v1-1 select true" | \ + sudo debconf-set-selections + yes | sudo apt-add-repository ppa:webupd8team/java + yes | sudo apt-get install oracle-java7-installer + fi; export PATH=/usr/lib/jvm/java-7-oracle/bin:$PATH ;; esac + if [ "$TRAVIS" != "true" ]; then + MAVEN_LOCAL_REPOSITORY=/var/maven_local_repository + MVN="$MVN -e -X --offline -Dmaven.repo.local=$MAVEN_LOCAL_REPOSITORY" + fi; + which java java -version } +# --batch-mode supresses download progress output that spams the logs. +MVN="mvn --batch-mode" + build_java() { + version=$1 + dir=java_$version # Java build needs `protoc`. internal_build_cpp - cd java && mvn test && mvn install - cd util && mvn test + cp -r java $dir + cd $dir && $MVN clean && $MVN test cd ../.. } +# The conformance tests are hard-coded to work with the $ROOT/java directory. +# So this can't run in parallel with two different sets of tests. build_java_with_conformance_tests() { # Java build needs `protoc`. internal_build_cpp - cd java && mvn test && mvn install - cd util && mvn test && mvn assembly:single + cd java && $MVN test && $MVN install + cd util && $MVN package assembly:single cd ../.. cd conformance && make test_java && cd .. } @@ -119,12 +147,12 @@ build_java_with_conformance_tests() { build_javanano() { # Java build needs `protoc`. internal_build_cpp - cd javanano && mvn test && cd .. + cd javanano && $MVN test && cd .. } build_java_jdk6() { use_java jdk6 - build_java + build_java jdk6 } build_java_jdk7() { use_java jdk7 @@ -132,7 +160,7 @@ build_java_jdk7() { } build_java_oracle7() { use_java oracle7 - build_java + build_java oracle7 } build_javanano_jdk6() { @@ -149,6 +177,9 @@ build_javanano_oracle7() { } internal_install_python_deps() { + if [ "$TRAVIS" != "true" ]; then + return; + fi # Install tox (OS X doesn't have pip). if [ $(uname -s) == "Darwin" ]; then sudo easy_install tox @@ -171,13 +202,7 @@ internal_objectivec_common () { # http://docs.travis-ci.com/user/osx-ci-environment/ # We don't use a before_install because we test multiple OSes. brew update - # xctool 0.2.8 seems to have a bug where it randomly kills tests saying - # they failed. Disabling the updates, but letting it report about being - # updates as a hint that this needs to eventually get re-enabled. - # https://github.com/facebook/xctool/issues/619 - # https://github.com/google/protobuf/issues/1232 - brew outdated xctool || true - #brew outdated xctool || brew upgrade xctool + brew outdated xctool || brew upgrade xctool # Reused the build script that takes care of configuring and ensuring things # are up to date. Xcode and conformance tests will be directly invoked. objectivec/DevTools/full_mac_build.sh \ @@ -284,6 +309,14 @@ build_javascript() { cd js && npm install && npm test && cd .. } +# Note: travis currently does not support testing more than one language so the +# .travis.yml cheats and claims to only be cpp. If they add multiple language +# support, this should probably get updated to install steps and/or +# rvm/gemfile/jdk/etc. entries rather than manually doing the work. + +# .travis.yml uses matrix.exclude to block the cases where app-get can't be +# use to install things. + # -------- main -------- if [ "$#" -ne 1 ]; then @@ -300,10 +333,10 @@ Usage: $0 { cpp | objectivec_osx | python | python_cpp | - ruby_19 | - ruby_20 | - ruby_21 | - ruby_22 | + ruby19 | + ruby20 | + ruby21 | + ruby22 | jruby } " exit 1