diff options
author | Feng Xiao <xfxyjwf@gmail.com> | 2015-12-11 17:09:20 -0800 |
---|---|---|
committer | Feng Xiao <xfxyjwf@gmail.com> | 2015-12-11 17:10:28 -0800 |
commit | e841bac4fcf47f809e089a70d5f84ac37b3883df (patch) | |
tree | d25dc5fc814db182c04c5f276ff1a609c5965a5a /src | |
parent | 99a6a95c751a28a3cc33dd2384959179f83f682c (diff) | |
download | protobuf-e841bac4fcf47f809e089a70d5f84ac37b3883df.tar.gz protobuf-e841bac4fcf47f809e089a70d5f84ac37b3883df.tar.bz2 protobuf-e841bac4fcf47f809e089a70d5f84ac37b3883df.zip |
Down-integrate from internal code base.
Diffstat (limited to 'src')
102 files changed, 6762 insertions, 2211 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index b7d64093..d684c937 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -148,15 +148,16 @@ nobase_include_HEADERS = \ google/protobuf/compiler/plugin.h \ google/protobuf/compiler/plugin.pb.h \ google/protobuf/compiler/cpp/cpp_generator.h \ + google/protobuf/compiler/csharp/csharp_generator.h \ + google/protobuf/compiler/csharp/csharp_names.h \ google/protobuf/compiler/java/java_generator.h \ google/protobuf/compiler/java/java_names.h \ google/protobuf/compiler/javanano/javanano_generator.h \ + google/protobuf/compiler/js/js_generator.h \ google/protobuf/compiler/objectivec/objectivec_generator.h \ google/protobuf/compiler/objectivec/objectivec_helpers.h \ google/protobuf/compiler/python/python_generator.h \ google/protobuf/compiler/ruby/ruby_generator.h \ - google/protobuf/compiler/csharp/csharp_generator.h \ - google/protobuf/compiler/csharp/csharp_names.h \ google/protobuf/util/type_resolver.h \ google/protobuf/util/field_comparator.h \ google/protobuf/util/field_mask_util.h \ @@ -276,6 +277,8 @@ libprotobuf_la_SOURCES = \ google/protobuf/util/internal/protostream_objectsource.h \ google/protobuf/util/internal/protostream_objectwriter.cc \ google/protobuf/util/internal/protostream_objectwriter.h \ + google/protobuf/util/internal/proto_writer.cc \ + google/protobuf/util/internal/proto_writer.h \ google/protobuf/util/internal/snake2camel_objectwriter.h \ google/protobuf/util/internal/structured_objectwriter.h \ google/protobuf/util/internal/testdata \ @@ -386,6 +389,7 @@ libprotoc_la_SOURCES = \ google/protobuf/compiler/java/java_string_field_lite.h \ google/protobuf/compiler/java/java_doc_comment.cc \ google/protobuf/compiler/java/java_doc_comment.h \ + google/protobuf/compiler/js/js_generator.cc \ google/protobuf/compiler/javanano/javanano_enum.cc \ google/protobuf/compiler/javanano/javanano_enum.h \ google/protobuf/compiler/javanano/javanano_enum_field.cc \ diff --git a/src/google/protobuf/any.cc b/src/google/protobuf/any.cc index 7351d377..f3ca06bf 100644 --- a/src/google/protobuf/any.cc +++ b/src/google/protobuf/any.cc @@ -65,9 +65,16 @@ bool AnyMetadata::UnpackTo(Message* message) const { } bool AnyMetadata::InternalIs(const Descriptor* descriptor) const { - return type_url_->GetNoArena( - &::google::protobuf::internal::GetEmptyString()) == - GetTypeUrl(descriptor); + 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; + } + return (0 == type_url.compare( + type_url.length() - full_name.length(), + full_name.length(), + full_name)); } bool ParseAnyTypeUrl(const string& type_url, string* full_type_name) { diff --git a/src/google/protobuf/any.h b/src/google/protobuf/any.h index f760ad5d..c8dbef13 100644 --- a/src/google/protobuf/any.h +++ b/src/google/protobuf/any.h @@ -75,7 +75,7 @@ extern const char kTypeGoogleProdComPrefix[]; // "type.googleprod.com/". // Get the proto type name from Any::type_url value. For example, passing // "type.googleapis.com/rpc.QueryOrigin" will return "rpc.QueryOrigin" in // *full_type_name. Returns false if type_url does not start with -// "type.googleapis.com". +// "type.googleapis.com" or "type.googleprod.com". bool ParseAnyTypeUrl(const string& type_url, string* full_type_name); // See if message is of type google.protobuf.Any, if so, return the descriptors diff --git a/src/google/protobuf/any.pb.cc b/src/google/protobuf/any.pb.cc index 321fd315..0bf523b3 100644 --- a/src/google/protobuf/any.pb.cc +++ b/src/google/protobuf/any.pb.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/port.h> #include <google/protobuf/stubs/once.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/wire_format_lite_inl.h> diff --git a/src/google/protobuf/any.proto b/src/google/protobuf/any.proto index 423699be..e8a18bc3 100644 --- a/src/google/protobuf/any.proto +++ b/src/google/protobuf/any.proto @@ -27,21 +27,22 @@ // 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 google.protobuf; -option java_generate_equals_and_hash = true; -option java_multiple_files = true; -option java_outer_classname = "AnyProto"; -option java_package = "com.google.protobuf"; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +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. // +// // JSON // ==== // The JSON representation of an `Any` value uses the regular @@ -62,8 +63,8 @@ option objc_class_prefix = "GPB"; // // If the embedded message type is well-known and has a custom JSON // representation, that representation will be embedded adding a field -// `value` which holds the custom JSON in addition to the the `@type` -// field. Example (for message [google.protobuf.Duration][google.protobuf.Duration]): +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): // // { // "@type": "type.googleapis.com/google.protobuf.Duration", @@ -80,7 +81,7 @@ 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`). - // * An HTTP GET on the URL must yield a [google.protobuf.Type][google.protobuf.Type] + // * 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 // URL, or have them precompiled into a binary to avoid any diff --git a/src/google/protobuf/api.pb.cc b/src/google/protobuf/api.pb.cc index ed90702c..e589a89d 100644 --- a/src/google/protobuf/api.pb.cc +++ b/src/google/protobuf/api.pb.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/port.h> #include <google/protobuf/stubs/once.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/wire_format_lite_inl.h> diff --git a/src/google/protobuf/api.proto b/src/google/protobuf/api.proto index 597a6497..dbe87b8f 100644 --- a/src/google/protobuf/api.proto +++ b/src/google/protobuf/api.proto @@ -27,6 +27,7 @@ // 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 google.protobuf; @@ -34,11 +35,11 @@ package google.protobuf; import "google/protobuf/source_context.proto"; import "google/protobuf/type.proto"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; option java_package = "com.google.protobuf"; option java_outer_classname = "ApiProto"; option java_multiple_files = true; option java_generate_equals_and_hash = true; -option csharp_namespace = "Google.Protobuf.WellKnownTypes"; option objc_class_prefix = "GPB"; // Api is a light-weight descriptor for a protocol buffer service. @@ -75,6 +76,7 @@ message Api { // be omitted. Zero major versions must only be used for // experimental, none-GA apis. // + // string version = 4; // Source context for the protocol buffer service represented by this @@ -141,7 +143,6 @@ message Method { // // package google.storage.v2; // service Storage { -// // (-- see AccessControl.GetAcl --) // rpc GetAcl(GetAclRequest) returns (Acl); // // // Get a data record. diff --git a/src/google/protobuf/arena.h b/src/google/protobuf/arena.h index 40c1e7c2..6a35183e 100644 --- a/src/google/protobuf/arena.h +++ b/src/google/protobuf/arena.h @@ -591,14 +591,12 @@ class LIBPROTOBUF_EXPORT Arena { // This is inside Arena because only Arena has the friend relationships // necessary to see the underlying generated code traits. template<typename T> - struct is_destructor_skippable : - public google::protobuf::internal::integral_constant<bool, - sizeof(InternalIsDestructorSkippableHelper::DestructorSkippable< - const T>(static_cast<const T*>(0))) == - sizeof(char) || - google::protobuf::internal::has_trivial_destructor<T>::value> { - }; - + struct is_destructor_skippable + : public google::protobuf::internal::integral_constant< + bool, + sizeof(InternalIsDestructorSkippableHelper::DestructorSkippable< + const T>(static_cast<const T*>(0))) == sizeof(char) || + google::protobuf::internal::has_trivial_destructor<T>::value> {}; // CreateMessage<T> requires that T supports arenas, but this private method // works whether or not T supports arenas. These are not exposed to user code diff --git a/src/google/protobuf/arenastring.h b/src/google/protobuf/arenastring.h index 1dacdc68..ef57033b 100755 --- a/src/google/protobuf/arenastring.h +++ b/src/google/protobuf/arenastring.h @@ -36,7 +36,6 @@ #include <google/protobuf/stubs/logging.h> #include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/fastmem.h> - #include <google/protobuf/arena.h> #include <google/protobuf/generated_message_util.h> diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index 26a4f0b0..deb3d0f1 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -271,15 +271,35 @@ class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector, // implements MultiFileErrorCollector ------------------------------ void AddError(const string& filename, int line, int column, const string& message) { + AddErrorOrWarning(filename, line, column, message, "error", std::cerr); + } + + void AddWarning(const string& filename, int line, int column, + const string& message) { + AddErrorOrWarning(filename, line, column, message, "warning", std::clog); + } + // implements io::ErrorCollector ----------------------------------- + void AddError(int line, int column, const string& message) { + AddError("input", line, column, message); + } + + void AddWarning(int line, int column, const string& message) { + AddErrorOrWarning("input", line, column, message, "warning", std::clog); + } + + private: + void AddErrorOrWarning( + const string& filename, int line, int column, + const string& message, const string& type, ostream& out) { // Print full path when running under MSVS string dfile; if (format_ == CommandLineInterface::ERROR_FORMAT_MSVS && tree_ != NULL && tree_->VirtualFileToDiskFile(filename, &dfile)) { - std::cerr << dfile; + out << dfile; } else { - std::cerr << filename; + out << filename; } // Users typically expect 1-based line/column numbers, so we add 1 @@ -288,24 +308,22 @@ class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector, // Allow for both GCC- and Visual-Studio-compatible output. switch (format_) { case CommandLineInterface::ERROR_FORMAT_GCC: - std::cerr << ":" << (line + 1) << ":" << (column + 1); + out << ":" << (line + 1) << ":" << (column + 1); break; case CommandLineInterface::ERROR_FORMAT_MSVS: - std::cerr << "(" << (line + 1) - << ") : error in column=" << (column + 1); + out << "(" << (line + 1) << ") : " + << type << " in column=" << (column + 1); break; } } - std::cerr << ": " << message << std::endl; - } - - // implements io::ErrorCollector ----------------------------------- - void AddError(int line, int column, const string& message) { - AddError("input", line, column, message); + if (type == "warning") { + out << ": warning: " << message << std::endl; + } else { + out << ": " << message << std::endl; + } } - private: const ErrorFormat format_; DiskSourceTree *tree_; }; @@ -1353,7 +1371,7 @@ void CommandLineInterface::PrintHelpText() { " defined in the given proto files. Groups share\n" " the same field number space with the parent \n" " message. Extension ranges are counted as \n" -" occupied fields numbers." +" occupied fields numbers.\n" << std::endl; if (!plugin_prefix_.empty()) { std::cerr << @@ -1443,6 +1461,7 @@ bool CommandLineInterface::GenerateDependencyManifestFile( for (int i = 0; i < parsed_files.size(); i++) { GetTransitiveDependencies(parsed_files[i], false, + false, &already_seen, file_set.mutable_file()); } @@ -1522,6 +1541,7 @@ bool CommandLineInterface::GeneratePluginOutput( for (int i = 0; i < parsed_files.size(); i++) { request.add_file_to_generate(parsed_files[i]->name()); GetTransitiveDependencies(parsed_files[i], + true, // Include json_name for plugins. true, // Include source code info. &already_seen, request.mutable_proto_file()); } @@ -1654,6 +1674,7 @@ bool CommandLineInterface::WriteDescriptorSet( set<const FileDescriptor*> already_seen; for (int i = 0; i < parsed_files.size(); i++) { GetTransitiveDependencies(parsed_files[i], + true, // Include json_name source_info_in_descriptor_set_, &already_seen, file_set.mutable_file()); } @@ -1665,6 +1686,7 @@ bool CommandLineInterface::WriteDescriptorSet( } FileDescriptorProto* file_proto = file_set.add_file(); parsed_files[i]->CopyTo(file_proto); + parsed_files[i]->CopyJsonNameTo(file_proto); if (source_info_in_descriptor_set_) { parsed_files[i]->CopySourceCodeInfoTo(file_proto); } @@ -1699,7 +1721,9 @@ bool CommandLineInterface::WriteDescriptorSet( } void CommandLineInterface::GetTransitiveDependencies( - const FileDescriptor* file, bool include_source_code_info, + const FileDescriptor* file, + bool include_json_name, + bool include_source_code_info, set<const FileDescriptor*>* already_seen, RepeatedPtrField<FileDescriptorProto>* output) { if (!already_seen->insert(file).second) { @@ -1709,13 +1733,18 @@ void CommandLineInterface::GetTransitiveDependencies( // Add all dependencies. for (int i = 0; i < file->dependency_count(); i++) { - GetTransitiveDependencies(file->dependency(i), include_source_code_info, + GetTransitiveDependencies(file->dependency(i), + include_json_name, + include_source_code_info, already_seen, output); } // Add this file. FileDescriptorProto* new_descriptor = output->Add(); file->CopyTo(new_descriptor); + if (include_json_name) { + file->CopyJsonNameTo(new_descriptor); + } if (include_source_code_info) { file->CopySourceCodeInfoTo(new_descriptor); } diff --git a/src/google/protobuf/compiler/command_line_interface.h b/src/google/protobuf/compiler/command_line_interface.h index 7e611c44..f196ffc5 100644 --- a/src/google/protobuf/compiler/command_line_interface.h +++ b/src/google/protobuf/compiler/command_line_interface.h @@ -262,8 +262,11 @@ class LIBPROTOC_EXPORT CommandLineInterface { // in order. Any files in *already_seen will not be added, and each file // added will be inserted into *already_seen. If include_source_code_info is // true then include the source code information in the FileDescriptorProtos. + // If include_json_name is true, populate the json_name field of + // FieldDescriptorProto for all fields. static void GetTransitiveDependencies( const FileDescriptor* file, + bool include_json_name, bool include_source_code_info, set<const FileDescriptor*>* already_seen, RepeatedPtrField<FileDescriptorProto>* output); diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc index fa792dae..4ce81a75 100644 --- a/src/google/protobuf/compiler/command_line_interface_unittest.cc +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -886,6 +886,10 @@ TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) { EXPECT_EQ("bar.proto", descriptor_set.file(0).name()); // Descriptor set should not have source code info. EXPECT_FALSE(descriptor_set.file(0).has_source_code_info()); + // Descriptor set should have json_name. + EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name()); + EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name()); + EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name()); } TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithDuplicates) { @@ -919,6 +923,10 @@ TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithDuplicates) { EXPECT_EQ("baz.proto", descriptor_set.file(2).name()); // Descriptor set should not have source code info. EXPECT_FALSE(descriptor_set.file(0).has_source_code_info()); + // Descriptor set should have json_name. + EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name()); + EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name()); + EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name()); } TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) { @@ -1413,6 +1421,18 @@ TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) { "Saw message type MockCodeGenerator_HasSourceCodeInfo: 1."); } +TEST_F(CommandLineInterfaceTest, PluginReceivesJsonName) { + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message MockCodeGenerator_HasJsonName {\n" + " optional int32 value = 1;\n" + "}\n"); + + Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto"); + + ExpectErrorSubstring("Saw json_name: 1"); +} + TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) { // Test what happens if the plugin isn't found. diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index 9aa41534..37e4bae4 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -252,6 +252,7 @@ void FileGenerator::GenerateSource(io::Printer* printer) { "#include <algorithm>\n" // for swap() "\n" "#include <google/protobuf/stubs/common.h>\n" + "#include <google/protobuf/stubs/port.h>\n" "#include <google/protobuf/stubs/once.h>\n" "#include <google/protobuf/io/coded_stream.h>\n" "#include <google/protobuf/wire_format_lite_inl.h>\n", diff --git a/src/google/protobuf/compiler/importer.cc b/src/google/protobuf/compiler/importer.cc index 8333684e..0d9093c0 100644 --- a/src/google/protobuf/compiler/importer.cc +++ b/src/google/protobuf/compiler/importer.cc @@ -185,6 +185,19 @@ void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddError( owner_->error_collector_->AddError(filename, line, column, message); } +void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddWarning( + const string& filename, + const string& element_name, + const Message* descriptor, + ErrorLocation location, + const string& message) { + if (owner_->error_collector_ == NULL) return; + + int line, column; + owner_->source_locations_.Find(descriptor, location, &line, &column); + owner_->error_collector_->AddWarning(filename, line, column, message); +} + // =================================================================== Importer::Importer(SourceTree* source_tree, diff --git a/src/google/protobuf/compiler/importer.h b/src/google/protobuf/compiler/importer.h index f010fd08..cc8fcc39 100644 --- a/src/google/protobuf/compiler/importer.h +++ b/src/google/protobuf/compiler/importer.h @@ -121,6 +121,12 @@ class LIBPROTOBUF_EXPORT SourceTreeDescriptorDatabase : public DescriptorDatabas ErrorLocation location, const string& message); + virtual void AddWarning(const string& filename, + const string& element_name, + const Message* descriptor, + ErrorLocation location, + const string& message); + private: SourceTreeDescriptorDatabase* owner_; }; @@ -188,6 +194,9 @@ class LIBPROTOBUF_EXPORT MultiFileErrorCollector { virtual void AddError(const string& filename, int line, int column, const string& message) = 0; + virtual void AddWarning(const string& filename, int line, int column, + const string& message) {} + private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MultiFileErrorCollector); }; diff --git a/src/google/protobuf/compiler/importer_unittest.cc b/src/google/protobuf/compiler/importer_unittest.cc index 33c9328f..be19aa2e 100644 --- a/src/google/protobuf/compiler/importer_unittest.cc +++ b/src/google/protobuf/compiler/importer_unittest.cc @@ -71,6 +71,7 @@ class MockErrorCollector : public MultiFileErrorCollector { ~MockErrorCollector() {} string text_; + string warning_text_; // implements ErrorCollector --------------------------------------- void AddError(const string& filename, int line, int column, @@ -78,6 +79,12 @@ class MockErrorCollector : public MultiFileErrorCollector { strings::SubstituteAndAppend(&text_, "$0:$1:$2: $3\n", filename, line, column, message); } + + void AddWarning(const string& filename, int line, int column, + const string& message) { + strings::SubstituteAndAppend(&warning_text_, "$0:$1:$2: $3\n", + filename, line, column, message); + } }; // ------------------------------------------------------------------- @@ -123,6 +130,7 @@ class ImporterTest : public testing::Test { // Return the collected error text string error() const { return error_collector_.text_; } + string warning() const { return error_collector_.warning_text_; } MockErrorCollector error_collector_; MockSourceTree source_tree_; diff --git a/src/google/protobuf/compiler/java/java_enum.cc b/src/google/protobuf/compiler/java/java_enum.cc index 8a09f3a8..5fc9b002 100644 --- a/src/google/protobuf/compiler/java/java_enum.cc +++ b/src/google/protobuf/compiler/java/java_enum.cc @@ -85,17 +85,10 @@ EnumGenerator::~EnumGenerator() {} void EnumGenerator::Generate(io::Printer* printer) { WriteEnumDocComment(printer, descriptor_); - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public enum $classname$\n" - " implements com.google.protobuf.ProtocolMessageEnum {\n", - "classname", descriptor_->name()); - } else { - printer->Print( - "public enum $classname$\n" - " implements com.google.protobuf.Internal.EnumLite {\n", - "classname", descriptor_->name()); - } + printer->Print( + "public enum $classname$\n" + " implements com.google.protobuf.ProtocolMessageEnum {\n", + "classname", descriptor_->name()); printer->Indent(); for (int i = 0; i < canonical_values_.size(); i++) { @@ -311,7 +304,6 @@ void EnumGenerator::Generate(io::Printer* printer) { "}\n" "\n"); - // index is only used for reflection; lite implementation does not need it printer->Print("private final int index;\n"); } 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 e8bf15d0..e3e87c58 100644 --- a/src/google/protobuf/compiler/java/java_enum_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.cc @@ -596,9 +596,7 @@ GenerateInterfaceMembers(io::Printer* printer) const { void RepeatedImmutableEnumFieldLiteGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - // TODO(dweis): Switch to IntList? - "private com.google.protobuf.Internal.ProtobufList<\n" - " java.lang.Integer> $name$_;\n" + "private com.google.protobuf.Internal.IntList $name$_;\n" "private static final com.google.protobuf.Internal.ListAdapter.Converter<\n" " java.lang.Integer, $type$> $name$_converter_ =\n" " new com.google.protobuf.Internal.ListAdapter.Converter<\n" @@ -623,7 +621,7 @@ GenerateMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$(int index) {\n" - " return $name$_converter_.convert($name$_.get(index));\n" + " return $name$_converter_.convert($name$_.getInt(index));\n" "}\n"); if (SupportUnknownEnumValue(descriptor_->file())) { WriteFieldDocComment(printer, descriptor_); @@ -635,7 +633,7 @@ GenerateMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public int get$capitalized_name$Value(int index) {\n" - " return $name$_.get(index);\n" + " return $name$_.getInt(index);\n" "}\n"); } @@ -649,7 +647,7 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private void ensure$capitalized_name$IsMutable() {\n" " if (!$is_mutable$) {\n" - " $name$_ = newProtobufList($name$_);\n" + " $name$_ = newIntList($name$_);\n" " }\n" "}\n"); WriteFieldDocComment(printer, descriptor_); @@ -660,7 +658,7 @@ GenerateMembers(io::Printer* printer) const { " throw new NullPointerException();\n" " }\n" " ensure$capitalized_name$IsMutable();\n" - " $name$_.set(index, value.getNumber());\n" + " $name$_.setInt(index, value.getNumber());\n" "}\n"); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, @@ -669,7 +667,7 @@ GenerateMembers(io::Printer* printer) const { " throw new NullPointerException();\n" " }\n" " ensure$capitalized_name$IsMutable();\n" - " $name$_.add(value.getNumber());\n" + " $name$_.addInt(value.getNumber());\n" "}\n"); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, @@ -677,13 +675,13 @@ GenerateMembers(io::Printer* printer) const { " java.lang.Iterable<? extends $type$> values) {\n" " ensure$capitalized_name$IsMutable();\n" " for ($type$ value : values) {\n" - " $name$_.add(value.getNumber());\n" + " $name$_.addInt(value.getNumber());\n" " }\n" "}\n"); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "private void clear$capitalized_name$() {\n" - " $name$_ = emptyProtobufList();\n" + " $name$_ = emptyIntList();\n" "}\n"); if (SupportUnknownEnumValue(descriptor_->file())) { @@ -692,13 +690,13 @@ GenerateMembers(io::Printer* printer) const { "private void set$capitalized_name$Value(\n" " int index, int value) {\n" " ensure$capitalized_name$IsMutable();\n" - " $name$_.set(index, value);\n" + " $name$_.setInt(index, value);\n" "}\n"); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "private void add$capitalized_name$Value(int value) {\n" " ensure$capitalized_name$IsMutable();\n" - " $name$_.add(value);\n" + " $name$_.addInt(value);\n" "}\n"); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, @@ -706,7 +704,7 @@ GenerateMembers(io::Printer* printer) const { " java.lang.Iterable<java.lang.Integer> values) {\n" " ensure$capitalized_name$IsMutable();\n" " for (int value : values) {\n" - " $name$_.add(value);\n" + " $name$_.addInt(value);\n" " }\n" "}\n"); } @@ -805,7 +803,7 @@ GenerateFieldBuilderInitializationCode(io::Printer* printer) const { void RepeatedImmutableEnumFieldLiteGenerator:: GenerateInitializationCode(io::Printer* printer) const { - printer->Print(variables_, "$name$_ = emptyProtobufList();\n"); + printer->Print(variables_, "$name$_ = emptyIntList();\n"); } void RepeatedImmutableEnumFieldLiteGenerator:: @@ -840,9 +838,9 @@ GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, "int rawValue = input.readEnum();\n" "if (!$is_mutable$) {\n" - " $name$_ = newProtobufList();\n" + " $name$_ = newIntList();\n" "}\n" - "$name$_.add(rawValue);\n"); + "$name$_.addInt(rawValue);\n"); } else { printer->Print(variables_, "int rawValue = input.readEnum();\n" @@ -855,9 +853,9 @@ GenerateParsingCode(io::Printer* printer) const { printer->Print(variables_, "} else {\n" " if (!$is_mutable$) {\n" - " $name$_ = newProtobufList();\n" + " $name$_ = newIntList();\n" " }\n" - " $name$_.add(rawValue);\n" + " $name$_.addInt(rawValue);\n" "}\n"); } } @@ -897,12 +895,12 @@ GenerateSerializationCode(io::Printer* printer) const { " output.writeRawVarint32($name$MemoizedSerializedSize);\n" "}\n" "for (int i = 0; i < $name$_.size(); i++) {\n" - " output.writeEnumNoTag($name$_.get(i));\n" + " output.writeEnumNoTag($name$_.getInt(i));\n" "}\n"); } else { printer->Print(variables_, "for (int i = 0; i < $name$_.size(); i++) {\n" - " output.writeEnum($number$, $name$_.get(i));\n" + " output.writeEnum($number$, $name$_.getInt(i));\n" "}\n"); } } @@ -917,7 +915,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, "for (int i = 0; i < $name$_.size(); i++) {\n" " dataSize += com.google.protobuf.CodedOutputStream\n" - " .computeEnumSizeNoTag($name$_.get(i));\n" + " .computeEnumSizeNoTag($name$_.getInt(i));\n" "}\n"); printer->Print( "size += dataSize;\n"); diff --git a/src/google/protobuf/compiler/java/java_enum_lite.cc b/src/google/protobuf/compiler/java/java_enum_lite.cc index 62186386..ed415eee 100644 --- a/src/google/protobuf/compiler/java/java_enum_lite.cc +++ b/src/google/protobuf/compiler/java/java_enum_lite.cc @@ -85,34 +85,26 @@ EnumLiteGenerator::~EnumLiteGenerator() {} void EnumLiteGenerator::Generate(io::Printer* printer) { WriteEnumDocComment(printer, descriptor_); - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public enum $classname$\n" - " implements com.google.protobuf.ProtocolMessageEnum {\n", - "classname", descriptor_->name()); - } else { - printer->Print( - "public enum $classname$\n" - " implements com.google.protobuf.Internal.EnumLite {\n", - "classname", descriptor_->name()); - } + printer->Print( + "public enum $classname$\n" + " implements com.google.protobuf.Internal.EnumLite {\n", + "classname", descriptor_->name()); printer->Indent(); for (int i = 0; i < canonical_values_.size(); i++) { map<string, string> vars; vars["name"] = canonical_values_[i]->name(); - vars["index"] = SimpleItoa(canonical_values_[i]->index()); vars["number"] = SimpleItoa(canonical_values_[i]->number()); WriteEnumValueDocComment(printer, canonical_values_[i]); if (canonical_values_[i]->options().deprecated()) { printer->Print("@java.lang.Deprecated\n"); } printer->Print(vars, - "$name$($index$, $number$),\n"); + "$name$($number$),\n"); } if (SupportUnknownEnumValue(descriptor_->file())) { - printer->Print("UNRECOGNIZED(-1, -1),\n"); + printer->Print("UNRECOGNIZED(-1),\n"); } printer->Print( @@ -145,15 +137,7 @@ void EnumLiteGenerator::Generate(io::Printer* printer) { printer->Print( "\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"); - } - printer->Print( + "public final int getNumber() {\n" " return value;\n" "}\n" "\n" @@ -193,7 +177,7 @@ void EnumLiteGenerator::Generate(io::Printer* printer) { printer->Print( "private final int value;\n\n" - "private $classname$(int index, int value) {\n", + "private $classname$(int value) {\n", "classname", descriptor_->name()); printer->Print( " this.value = value;\n" diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc index 68b47ee1..c8172330 100644 --- a/src/google/protobuf/compiler/java/java_file.cc +++ b/src/google/protobuf/compiler/java/java_file.cc @@ -42,6 +42,7 @@ #include <google/protobuf/compiler/java/java_context.h> #include <google/protobuf/compiler/java/java_enum.h> +#include <google/protobuf/compiler/java/java_enum_lite.h> #include <google/protobuf/compiler/java/java_extension.h> #include <google/protobuf/compiler/java/java_generator_factory.h> #include <google/protobuf/compiler/java/java_helpers.h> @@ -281,8 +282,13 @@ void FileGenerator::Generate(io::Printer* printer) { if (!MultipleJavaFiles(file_, immutable_api_)) { for (int i = 0; i < file_->enum_type_count(); i++) { - EnumGenerator(file_->enum_type(i), immutable_api_, context_.get()) - .Generate(printer); + if (HasDescriptorMethods(file_)) { + EnumGenerator(file_->enum_type(i), immutable_api_, context_.get()) + .Generate(printer); + } else { + EnumLiteGenerator(file_->enum_type(i), immutable_api_, context_.get()) + .Generate(printer); + } } for (int i = 0; i < file_->message_type_count(); i++) { message_generators_[i]->GenerateInterface(printer); @@ -543,13 +549,23 @@ void FileGenerator::GenerateSiblings(const string& package_dir, vector<string>* file_list) { if (MultipleJavaFiles(file_, immutable_api_)) { for (int i = 0; i < file_->enum_type_count(); i++) { - EnumGenerator generator(file_->enum_type(i), immutable_api_, - context_.get()); - GenerateSibling<EnumGenerator>(package_dir, java_package_, - file_->enum_type(i), - context, file_list, "", - &generator, - &EnumGenerator::Generate); + if (HasDescriptorMethods(file_)) { + EnumGenerator generator(file_->enum_type(i), immutable_api_, + context_.get()); + GenerateSibling<EnumGenerator>(package_dir, java_package_, + file_->enum_type(i), + context, file_list, "", + &generator, + &EnumGenerator::Generate); + } else { + EnumLiteGenerator generator(file_->enum_type(i), immutable_api_, + context_.get()); + GenerateSibling<EnumLiteGenerator>(package_dir, java_package_, + file_->enum_type(i), + context, file_list, "", + &generator, + &EnumLiteGenerator::Generate); + } } for (int i = 0; i < file_->message_type_count(); i++) { if (immutable_api_) { 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 392333b8..5a7bf82d 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc @@ -87,11 +87,13 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, (*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()"; - (*variables)["repeated_index_get"] = - (*variables)["name"] + "_.get" + capitalized_type + "(index)"; + (*variables)["repeated_get"] = + (*variables)["name"] + "_.get" + capitalized_type; (*variables)["repeated_add"] = (*variables)["name"] + "_.add" + capitalized_type; (*variables)["repeated_set"] = @@ -102,11 +104,11 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, "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_index_get"] = - (*variables)["name"] + "_.get(index)"; + (*variables)["repeated_get"] = (*variables)["name"] + "_.get"; (*variables)["repeated_add"] = (*variables)["name"] + "_.add"; (*variables)["repeated_set"] = (*variables)["name"] + "_.set"; } @@ -629,7 +631,7 @@ GenerateMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$(int index) {\n" - " return $repeated_index_get$;\n" + " return $repeated_get$(index);\n" "}\n"); if (descriptor_->options().packed() && @@ -773,6 +775,9 @@ GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { void RepeatedImmutablePrimitiveFieldLiteGenerator:: GenerateParsingCode(io::Printer* printer) const { + // TODO(dweis): Scan the input buffer to count, then initialize + // appropriately. + // TODO(dweis): Scan the input buffer to count and ensure capacity. printer->Print(variables_, "if (!$is_mutable$) {\n" " $name$_ = $new_list$();\n" @@ -785,8 +790,21 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const { printer->Print(variables_, "int length = input.readRawVarint32();\n" "int limit = input.pushLimit(length);\n" - "if (!$is_mutable$ && input.getBytesUntilLimit() > 0) {\n" - " $name$_ = $new_list$();\n" + "if (!$is_mutable$ && input.getBytesUntilLimit() > 0) {\n"); + + int fixed_size = FixedSize(GetType(descriptor_)); + if (fixed_size == -1) { + // TODO(dweis): Scan the input buffer to count, then initialize + // appropriately. + printer->Print(variables_, + " $name$_ = $new_list$();\n"); + } else { + printer->Print(variables_, + " $name$_ = $new_list_with_capacity$(length/$fixed_size$);\n"); + } + + // TODO(dweis): Scan the input buffer to count and ensure capacity. + printer->Print(variables_, "}\n" "while (input.getBytesUntilLimit() > 0) {\n" " $repeated_add$(input.read$capitalized_type$());\n" @@ -814,12 +832,12 @@ GenerateSerializationCode(io::Printer* printer) const { " output.writeRawVarint32($name$MemoizedSerializedSize);\n" "}\n" "for (int i = 0; i < $name$_.size(); i++) {\n" - " output.write$capitalized_type$NoTag($name$_.get(i));\n" + " output.write$capitalized_type$NoTag($repeated_get$(i));\n" "}\n"); } else { printer->Print(variables_, "for (int i = 0; i < $name$_.size(); i++) {\n" - " output.write$capitalized_type$($number$, $name$_.get(i));\n" + " output.write$capitalized_type$($number$, $repeated_get$(i));\n" "}\n"); } } @@ -835,7 +853,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, "for (int i = 0; i < $name$_.size(); i++) {\n" " dataSize += com.google.protobuf.CodedOutputStream\n" - " .compute$capitalized_type$SizeNoTag($name$_.get(i));\n" + " .compute$capitalized_type$SizeNoTag($repeated_get$(i));\n" "}\n"); } else { printer->Print(variables_, diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc index 72ebaeca..7f757e47 100644 --- a/src/google/protobuf/compiler/java/java_string_field.cc +++ b/src/google/protobuf/compiler/java/java_string_field.cc @@ -405,7 +405,7 @@ void ImmutableStringFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { if (CheckUtf8(descriptor_)) { printer->Print(variables_, - "String s = input.readStringRequireUtf8();\n" + "java.lang.String s = input.readStringRequireUtf8();\n" "$set_has_field_bit_message$\n" "$name$_ = s;\n"); } else if (!HasDescriptorMethods(descriptor_->file())) { @@ -414,7 +414,7 @@ GenerateParsingCode(io::Printer* printer) const { // spurious intermediary ByteString allocations, cutting overall allocations // in half. printer->Print(variables_, - "String s = input.readString();\n" + "java.lang.String s = input.readString();\n" "$set_has_field_bit_message$\n" "$name$_ = s;\n"); } else { @@ -665,7 +665,7 @@ void ImmutableStringOneofFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { if (CheckUtf8(descriptor_)) { printer->Print(variables_, - "String s = input.readStringRequireUtf8();\n" + "java.lang.String s = input.readStringRequireUtf8();\n" "$set_oneof_case_message$;\n" "$oneof_name$_ = s;\n"); } else if (!HasDescriptorMethods(descriptor_->file())) { @@ -674,7 +674,7 @@ GenerateParsingCode(io::Printer* printer) const { // spurious intermediary ByteString allocations, cutting overall allocations // in half. printer->Print(variables_, - "String s = input.readString();\n" + "java.lang.String s = input.readString();\n" "$set_oneof_case_message$;\n" "$oneof_name$_ = s;\n"); } else { @@ -773,12 +773,6 @@ GenerateMembers(io::Printer* printer) const { " get$capitalized_name$Bytes(int index) {\n" " return $name$_.getByteString(index);\n" "}\n"); - - if (descriptor_->options().packed() && - HasGeneratedMethods(descriptor_->containing_type())) { - printer->Print(variables_, - "private int $name$MemoizedSerializedSize = -1;\n"); - } } void RepeatedImmutableStringFieldGenerator:: @@ -939,14 +933,14 @@ void RepeatedImmutableStringFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { if (CheckUtf8(descriptor_)) { printer->Print(variables_, - "String s = input.readStringRequireUtf8();\n"); + "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_, - "String s = input.readString();\n"); + "java.lang.String s = input.readString();\n"); } else { printer->Print(variables_, "com.google.protobuf.ByteString bs = input.readBytes();\n"); @@ -966,30 +960,6 @@ GenerateParsingCode(io::Printer* printer) const { } void RepeatedImmutableStringFieldGenerator:: -GenerateParsingCodeFromPacked(io::Printer* printer) const { - printer->Print(variables_, - "int length = input.readRawVarint32();\n" - "int limit = input.pushLimit(length);\n" - "if (!$get_mutable_bit_parser$ && input.getBytesUntilLimit() > 0) {\n" - " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" - " $set_mutable_bit_parser$;\n" - "}\n" - "while (input.getBytesUntilLimit() > 0) {\n"); - if (CheckUtf8(descriptor_)) { - printer->Print(variables_, - " String s = input.readStringRequireUtf8();\n"); - } else { - printer->Print(variables_, - " String s = input.readString();\n"); - } - printer->Print(variables_, - " $name$.add(s);\n"); - printer->Print(variables_, - "}\n" - "input.popLimit(limit);\n"); -} - -void RepeatedImmutableStringFieldGenerator:: GenerateParsingDoneCode(io::Printer* printer) const { printer->Print(variables_, "if ($get_mutable_bit_parser$) {\n" @@ -999,21 +969,10 @@ GenerateParsingDoneCode(io::Printer* printer) const { void RepeatedImmutableStringFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { - if (descriptor_->options().packed()) { - printer->Print(variables_, - "if (get$capitalized_name$List().size() > 0) {\n" - " output.writeRawVarint32($tag$);\n" - " output.writeRawVarint32($name$MemoizedSerializedSize);\n" - "}\n" - "for (int i = 0; i < $name$_.size(); i++) {\n" - " writeStringNoTag(output, $name$_.getRaw(i));\n" - "}\n"); - } else { - printer->Print(variables_, - "for (int i = 0; i < $name$_.size(); i++) {\n" - " $writeString$(output, $number$, $name$_.getRaw(i));\n" - "}\n"); - } + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " $writeString$(output, $number$, $name$_.getRaw(i));\n" + "}\n"); } void RepeatedImmutableStringFieldGenerator:: @@ -1031,23 +990,8 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print( "size += dataSize;\n"); - if (descriptor_->options().packed()) { - printer->Print(variables_, - "if (!get$capitalized_name$List().isEmpty()) {\n" - " size += $tag_size$;\n" - " size += com.google.protobuf.CodedOutputStream\n" - " .computeInt32SizeNoTag(dataSize);\n" - "}\n"); - } else { - printer->Print(variables_, - "size += $tag_size$ * get$capitalized_name$List().size();\n"); - } - - // cache the data size for packed fields. - if (descriptor_->options().packed()) { - printer->Print(variables_, - "$name$MemoizedSerializedSize = dataSize;\n"); - } + printer->Print(variables_, + "size += $tag_size$ * get$capitalized_name$List().size();\n"); printer->Outdent(); printer->Print("}\n"); diff --git a/src/google/protobuf/compiler/java/java_string_field.h b/src/google/protobuf/compiler/java/java_string_field.h index 1ea44dec..a3b57351 100644 --- a/src/google/protobuf/compiler/java/java_string_field.h +++ b/src/google/protobuf/compiler/java/java_string_field.h @@ -131,7 +131,6 @@ class RepeatedImmutableStringFieldGenerator : public ImmutableFieldGenerator { void GenerateMergingCode(io::Printer* printer) const; void GenerateBuildingCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; - void GenerateParsingCodeFromPacked(io::Printer* printer) const; void GenerateParsingDoneCode(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_string_field_lite.cc b/src/google/protobuf/compiler/java/java_string_field_lite.cc index 092e3c29..eb5964bd 100644 --- a/src/google/protobuf/compiler/java/java_string_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_string_field_lite.cc @@ -647,12 +647,6 @@ GenerateMembers(io::Printer* printer) const { " $name$_.get(index));\n" "}\n"); - if (descriptor_->options().packed() && - HasGeneratedMethods(descriptor_->containing_type())) { - printer->Print(variables_, - "private int $name$MemoizedSerializedSize = -1;\n"); - } - printer->Print(variables_, "private void ensure$capitalized_name$IsMutable() {\n" " if (!$is_mutable$) {\n" @@ -835,29 +829,6 @@ GenerateParsingCode(io::Printer* printer) const { } void RepeatedImmutableStringFieldLiteGenerator:: -GenerateParsingCodeFromPacked(io::Printer* printer) const { - printer->Print(variables_, - "int length = input.readRawVarint32();\n" - "int limit = input.pushLimit(length);\n" - "if (!$is_mutable$ && input.getBytesUntilLimit() > 0) {\n" - " $name$_ = com.google.protobuf.GeneratedMessageLite.newProtobufList();\n" - "}\n" - "while (input.getBytesUntilLimit() > 0) {\n"); - if (CheckUtf8(descriptor_)) { - printer->Print(variables_, - " String s = input.readStringRequireUtf8();\n"); - } else { - printer->Print(variables_, - " String s = input.readString();\n"); - } - printer->Print(variables_, - " $name$.add(s);\n"); - printer->Print(variables_, - "}\n" - "input.popLimit(limit);\n"); -} - -void RepeatedImmutableStringFieldLiteGenerator:: GenerateParsingDoneCode(io::Printer* printer) const { printer->Print(variables_, "if ($is_mutable$) {\n" @@ -870,21 +841,10 @@ GenerateSerializationCode(io::Printer* printer) const { // Lite runtime should reduce allocations by serializing the string directly. // This avoids spurious intermediary ByteString allocations, cutting overall // allocations in half. - if (descriptor_->options().packed()) { - printer->Print(variables_, - "if (get$capitalized_name$List().size() > 0) {\n" - " output.writeRawVarint32($tag$);\n" - " output.writeRawVarint32($name$MemoizedSerializedSize);\n" - "}\n" - "for (int i = 0; i < $name$_.size(); i++) {\n" - " output.writeStringNoTag($name$_.get(i));\n" - "}\n"); - } else { - printer->Print(variables_, - "for (int i = 0; i < $name$_.size(); i++) {\n" - " output.writeString($number$, $name$_.get(i));\n" - "}\n"); - } + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.writeString($number$, $name$_.get(i));\n" + "}\n"); } void RepeatedImmutableStringFieldLiteGenerator:: @@ -906,23 +866,9 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print( "size += dataSize;\n"); - if (descriptor_->options().packed()) { - printer->Print(variables_, - "if (!get$capitalized_name$List().isEmpty()) {\n" - " size += $tag_size$;\n" - " size += com.google.protobuf.CodedOutputStream\n" - " .computeInt32SizeNoTag(dataSize);\n" - "}\n"); - } else { - printer->Print(variables_, - "size += $tag_size$ * get$capitalized_name$List().size();\n"); - } - // cache the data size for packed fields. - if (descriptor_->options().packed()) { - printer->Print(variables_, - "$name$MemoizedSerializedSize = dataSize;\n"); - } + printer->Print(variables_, + "size += $tag_size$ * get$capitalized_name$List().size();\n"); printer->Outdent(); printer->Print("}\n"); 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 9d93b307..4d9b4bd7 100644 --- a/src/google/protobuf/compiler/java/java_string_field_lite.h +++ b/src/google/protobuf/compiler/java/java_string_field_lite.h @@ -129,7 +129,6 @@ class RepeatedImmutableStringFieldLiteGenerator void GenerateMergingCode(io::Printer* printer) const; void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; void GenerateParsingCode(io::Printer* printer) const; - void GenerateParsingCodeFromPacked(io::Printer* printer) const; void GenerateParsingDoneCode(io::Printer* printer) const; void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/js/js_generator.cc b/src/google/protobuf/compiler/js/js_generator.cc new file mode 100755 index 00000000..1e308464 --- /dev/null +++ b/src/google/protobuf/compiler/js/js_generator.cc @@ -0,0 +1,2620 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <google/protobuf/compiler/js/js_generator.h> + +#include <assert.h> +#include <algorithm> +#include <limits> +#include <map> +#include <memory> +#ifndef _SHARED_PTR_H +#include <google/protobuf/stubs/shared_ptr.h> +#endif +#include <string> +#include <utility> +#include <vector> + +#include <google/protobuf/stubs/logging.h> +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/stringprintf.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/io/zero_copy_stream.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/descriptor.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace js { + +// Sorted list of JavaScript keywords. These cannot be used as names. If they +// appear, we prefix them with "pb_". +const char* kKeyword[] = { + "abstract", + "boolean", + "break", + "byte", + "case", + "catch", + "char", + "class", + "const", + "continue", + "debugger", + "default", + "delete", + "do", + "double", + "else", + "enum", + "export", + "extends", + "false", + "final", + "finally", + "float", + "for", + "function", + "goto", + "if", + "implements", + "import", + "in", + "instanceof", + "int", + "interface", + "long", + "native", + "new", + "null", + "package", + "private", + "protected", + "public", + "return", + "short", + "static", + "super", + "switch", + "synchronized", + "this", + "throw", + "throws", + "transient", + "try", + "typeof", + "var", + "void", + "volatile", + "while", + "with", +}; + +static const int kNumKeyword = sizeof(kKeyword) / sizeof(char*); + +namespace { + +bool IsReserved(const string& ident) { + for (int i = 0; i < kNumKeyword; i++) { + if (ident == kKeyword[i]) { + return true; + } + } + return false; +} + +// Returns a copy of |filename| with any trailing ".protodevel" or ".proto +// suffix stripped. +string StripProto(const string& filename) { + const char* suffix = HasSuffixString(filename, ".protodevel") + ? ".protodevel" : ".proto"; + return StripSuffixString(filename, suffix); +} + +// Returns the fully normalized JavaScript path for the given +// file descriptor's package. +string GetPath(const GeneratorOptions& options, + const FileDescriptor* file) { + if (!options.namespace_prefix.empty()) { + return options.namespace_prefix; + } else if (!file->package().empty()) { + return "proto." + file->package(); + } else { + return "proto"; + } +} + +// Forward declare, so that GetPrefix can call this method, +// which in turn, calls GetPrefix. +string GetPath(const GeneratorOptions& options, + const Descriptor* descriptor); + +// Returns the path prefix for a message or enumeration that +// lives under the given file and containing type. +string GetPrefix(const GeneratorOptions& options, + const FileDescriptor* file_descriptor, + const Descriptor* containing_type) { + string prefix = ""; + + if (containing_type == NULL) { + prefix = GetPath(options, file_descriptor); + } else { + prefix = GetPath(options, containing_type); + } + + if (!prefix.empty()) { + prefix += "."; + } + + return prefix; +} + + +// Returns the fully normalized JavaScript path for the given +// message descriptor. +string GetPath(const GeneratorOptions& options, + const Descriptor* descriptor) { + return GetPrefix( + options, descriptor->file(), + descriptor->containing_type()) + descriptor->name(); +} + + +// Returns the fully normalized JavaScript path for the given +// field's containing message descriptor. +string GetPath(const GeneratorOptions& options, + const FieldDescriptor* descriptor) { + return GetPath(options, descriptor->containing_type()); +} + +// Returns the fully normalized JavaScript path for the given +// enumeration descriptor. +string GetPath(const GeneratorOptions& options, + const EnumDescriptor* enum_descriptor) { + return GetPrefix( + options, enum_descriptor->file(), + enum_descriptor->containing_type()) + enum_descriptor->name(); +} + + +// Returns the fully normalized JavaScript path for the given +// enumeration value descriptor. +string GetPath(const GeneratorOptions& options, + const EnumValueDescriptor* value_descriptor) { + return GetPath( + options, + value_descriptor->type()) + "." + value_descriptor->name(); +} + +// - Object field name: LOWER_UNDERSCORE -> LOWER_CAMEL, except for group fields +// (UPPER_CAMEL -> LOWER_CAMEL), with "List" (or "Map") appended if appropriate, +// and with reserved words triggering a "pb_" prefix. +// - Getters/setters: LOWER_UNDERSCORE -> UPPER_CAMEL, except for group fields +// (use the name directly), then append "List" if appropriate, then append "$" +// if resulting name is equal to a reserved word. +// - Enums: just uppercase. + +// Locale-independent version of ToLower that deals only with ASCII A-Z. +char ToLowerASCII(char c) { + if (c >= 'A' && c <= 'Z') { + return (c - 'A') + 'a'; + } else { + return c; + } +} + +vector<string> ParseLowerUnderscore(const string& input) { + vector<string> words; + string running = ""; + for (int i = 0; i < input.size(); i++) { + if (input[i] == '_') { + if (!running.empty()) { + words.push_back(running); + running.clear(); + } + } else { + running += ToLowerASCII(input[i]); + } + } + if (!running.empty()) { + words.push_back(running); + } + return words; +} + +vector<string> ParseUpperCamel(const string& input) { + vector<string> words; + string running = ""; + for (int i = 0; i < input.size(); i++) { + if (input[i] >= 'A' && input[i] <= 'Z' && !running.empty()) { + words.push_back(running); + running.clear(); + } + running += ToLowerASCII(input[i]); + } + if (!running.empty()) { + words.push_back(running); + } + return words; +} + +string ToLowerCamel(const vector<string>& words) { + string result; + for (int i = 0; i < words.size(); i++) { + string word = words[i]; + if (i == 0 && (word[0] >= 'A' && word[0] <= 'Z')) { + word[0] = (word[0] - 'A') + 'a'; + } else if (i != 0 && (word[0] >= 'a' && word[0] <= 'z')) { + word[0] = (word[0] - 'a') + 'A'; + } + result += word; + } + return result; +} + +string ToUpperCamel(const vector<string>& words) { + string result; + for (int i = 0; i < words.size(); i++) { + string word = words[i]; + if (word[0] >= 'a' && word[0] <= 'z') { + word[0] = (word[0] - 'a') + 'A'; + } + result += word; + } + return result; +} + +// Based on code from descriptor.cc (Thanks Kenton!) +// Uppercases the entire string, turning ValueName into +// VALUENAME. +string ToEnumCase(const string& input) { + string result; + result.reserve(input.size()); + + for (int i = 0; i < input.size(); i++) { + if ('a' <= input[i] && input[i] <= 'z') { + result.push_back(input[i] - 'a' + 'A'); + } else { + result.push_back(input[i]); + } + } + + return result; +} + +string ToFileName(const string& input) { + string result; + result.reserve(input.size()); + + for (int i = 0; i < input.size(); i++) { + if ('A' <= input[i] && input[i] <= 'Z') { + result.push_back(input[i] - 'A' + 'a'); + } else { + result.push_back(input[i]); + } + } + + return result; +} + +// Returns the message/response ID, if set. +string GetMessageId(const Descriptor* desc) { + return string(); +} + + +// Used inside Google only -- do not remove. +bool IsResponse(const Descriptor* desc) { return false; } +bool IgnoreField(const FieldDescriptor* field) { return false; } + + +// Does JSPB ignore this entire oneof? True only if all fields are ignored. +bool IgnoreOneof(const OneofDescriptor* oneof) { + for (int i = 0; i < oneof->field_count(); i++) { + if (!IgnoreField(oneof->field(i))) { + return false; + } + } + return true; +} + +string JSIdent(const FieldDescriptor* field, + bool is_upper_camel, + bool is_map) { + string result; + if (field->type() == FieldDescriptor::TYPE_GROUP) { + result = is_upper_camel ? + ToUpperCamel(ParseUpperCamel(field->message_type()->name())) : + ToLowerCamel(ParseUpperCamel(field->message_type()->name())); + } else { + result = is_upper_camel ? + ToUpperCamel(ParseLowerUnderscore(field->name())) : + ToLowerCamel(ParseLowerUnderscore(field->name())); + } + if (is_map) { + result += "Map"; + } else if (field->is_repeated()) { + result += "List"; + } + return result; +} + +string JSObjectFieldName(const FieldDescriptor* field) { + string name = JSIdent( + field, + /* is_upper_camel = */ false, + /* is_map = */ false); + if (IsReserved(name)) { + name = "pb_" + name; + } + return name; +} + +// 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 name = JSIdent(field, + /* is_upper_camel = */ true, + /* is_map = */ false); + if (name == "Extension" || name == "JsPbMessageId") { + // Avoid conflicts with base-class names. + name += "$"; + } + return name; +} + +string JSMapGetterName(const FieldDescriptor* field) { + return JSIdent(field, + /* is_upper_camel = */ true, + /* is_map = */ true); +} + + + +string JSOneofName(const OneofDescriptor* oneof) { + return ToUpperCamel(ParseLowerUnderscore(oneof->name())); +} + +// Returns the index corresponding to this field in the JSPB array (underlying +// data storage array). +string JSFieldIndex(const FieldDescriptor* field) { + // Determine whether this field is a member of a group. Group fields are a bit + // wonky: their "containing type" is a message type created just for the + // group, and that type's parent type has a field with the group-message type + // as its message type and TYPE_GROUP as its field type. For such fields, the + // index we use is relative to the field number of the group submessage field. + // For all other fields, we just use the field number. + const Descriptor* containing_type = field->containing_type(); + const Descriptor* parent_type = containing_type->containing_type(); + if (parent_type != NULL) { + for (int i = 0; i < parent_type->field_count(); i++) { + if (parent_type->field(i)->type() == FieldDescriptor::TYPE_GROUP && + parent_type->field(i)->message_type() == containing_type) { + return SimpleItoa(field->number() - parent_type->field(i)->number()); + } + } + } + return SimpleItoa(field->number()); +} + +string JSOneofIndex(const OneofDescriptor* oneof) { + int index = -1; + for (int i = 0; i < oneof->containing_type()->oneof_decl_count(); i++) { + const OneofDescriptor* o = oneof->containing_type()->oneof_decl(i); + // If at least one field in this oneof is not JSPB-ignored, count the oneof. + for (int j = 0; j < o->field_count(); j++) { + const FieldDescriptor* f = o->field(j); + if (!IgnoreField(f)) { + index++; + break; // inner loop + } + } + if (o == oneof) { + break; + } + } + 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. +uint16_t DecodeUTF8Codepoint(uint8_t* bytes, size_t* length) { + if (*length == 0) { + return 0; + } + size_t expected = 0; + if ((*bytes & 0x80) == 0) { + expected = 1; + } else if ((*bytes & 0xe0) == 0xc0) { + expected = 2; + } else if ((*bytes & 0xf0) == 0xe0) { + expected = 3; + } else { + // Too long -- don't accept. + *length = 0; + return 0; + } + + if (*length < expected) { + // Not enough bytes -- don't accept. + *length = 0; + return 0; + } + + *length = expected; + switch (expected) { + case 1: return bytes[0]; + case 2: return ((bytes[0] & 0x1F) << 6) | + ((bytes[1] & 0x3F) << 0); + case 3: return ((bytes[0] & 0x0F) << 12) | + ((bytes[1] & 0x3F) << 6) | + ((bytes[2] & 0x3F) << 0); + default: return 0; + } +} + +// 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; + size_t decoded = 0; + for (size_t i = 0; i < in.size(); i += decoded) { + uint16_t codepoint = 0; + if (is_utf8) { + // Decode the next UTF-8 codepoint. + size_t have_bytes = in.size() - i; + uint8_t bytes[3] = { + static_cast<uint8_t>(in[i]), + static_cast<uint8_t>(((i + 1) < in.size()) ? in[i + 1] : 0), + static_cast<uint8_t>(((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<uint16_t>(static_cast<uint8_t>(in[i])); + decoded = 1; + } + + // 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'); + + 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; + default: + // All other non-ASCII codepoints are escaped. + // Original codegen uses hex for >= 0x100 and octal for others. + if (codepoint >= 0x20 && codepoint <= 0x7e) { + result += static_cast<char>(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)); + } + } + } + break; + } + } + return result; +} + +string EscapeBase64(const string& in) { + static const char* kAlphabet = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + string result; + + for (size_t i = 0; i < in.size(); i += 3) { + int value = (in[i] << 16) | + (((i + 1) < in.size()) ? (in[i + 1] << 8) : 0) | + (((i + 2) < in.size()) ? (in[i + 2] << 0) : 0); + result += kAlphabet[(value >> 18) & 0x3f]; + result += kAlphabet[(value >> 12) & 0x3f]; + if ((i + 1) < in.size()) { + result += kAlphabet[(value >> 6) & 0x3f]; + } else { + result += '='; + } + if ((i + 2) < in.size()) { + result += kAlphabet[(value >> 0) & 0x3f]; + } else { + result += '='; + } + } + + return result; +} + +// Post-process the result of SimpleFtoa/SimpleDtoa to *exactly* match the +// original codegen's formatting (which is just .toString() on java.lang.Double +// or java.lang.Float). +string PostProcessFloat(string result) { + // If inf, -inf or nan, replace with +Infinity, -Infinity or NaN. + if (result == "inf") { + return "Infinity"; + } else if (result == "-inf") { + return "-Infinity"; + } else if (result == "nan") { + return "NaN"; + } + + // If scientific notation (e.g., "1e10"), (i) capitalize the "e", (ii) + // ensure that the mantissa (portion prior to the "e") has at least one + // fractional digit (after the decimal point), and (iii) strip any unnecessary + // leading zeroes and/or '+' signs from the exponent. + string::size_type exp_pos = result.find('e'); + if (exp_pos != string::npos) { + string mantissa = result.substr(0, exp_pos); + string exponent = result.substr(exp_pos + 1); + + // Add ".0" to mantissa if no fractional part exists. + if (mantissa.find('.') == string::npos) { + mantissa += ".0"; + } + + // Strip the sign off the exponent and store as |exp_neg|. + bool exp_neg = false; + if (!exponent.empty() && exponent[0] == '+') { + exponent = exponent.substr(1); + } else if (!exponent.empty() && exponent[0] == '-') { + exp_neg = true; + exponent = exponent.substr(1); + } + + // Strip any leading zeroes off the exponent. + while (exponent.size() > 1 && exponent[0] == '0') { + exponent = exponent.substr(1); + } + + return mantissa + "E" + string(exp_neg ? "-" : "") + exponent; + } + + // Otherwise, this is an ordinary decimal number. Append ".0" if result has no + // decimal/fractional part in order to match output of original codegen. + if (result.find('.') == string::npos) { + result += ".0"; + } + + return result; +} + +string FloatToString(float value) { + string result = SimpleFtoa(value); + return PostProcessFloat(result); +} + +string DoubleToString(double value) { + string result = SimpleDtoa(value); + return PostProcessFloat(result); +} + +string MaybeNumberString(const FieldDescriptor* field, const string& orig) { + return orig; +} + +string JSFieldDefault(const FieldDescriptor* field) { + assert(field->has_default_value()); + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + return MaybeNumberString( + field, SimpleItoa(field->default_value_int32())); + case FieldDescriptor::CPPTYPE_UINT32: + // The original codegen is in Java, and Java protobufs store unsigned + // integer values as signed integer values. In order to exactly match the + // output, we need to reinterpret as base-2 signed. Ugh. + return MaybeNumberString( + field, SimpleItoa(static_cast<int32>(field->default_value_uint32()))); + case FieldDescriptor::CPPTYPE_INT64: + return MaybeNumberString( + field, SimpleItoa(field->default_value_int64())); + case FieldDescriptor::CPPTYPE_UINT64: + // See above note for uint32 -- reinterpreting as signed. + return MaybeNumberString( + field, SimpleItoa(static_cast<int64>(field->default_value_uint64()))); + case FieldDescriptor::CPPTYPE_ENUM: + return SimpleItoa(field->default_value_enum()->number()); + case FieldDescriptor::CPPTYPE_BOOL: + return field->default_value_bool() ? "true" : "false"; + case FieldDescriptor::CPPTYPE_FLOAT: + return FloatToString(field->default_value_float()); + case FieldDescriptor::CPPTYPE_DOUBLE: + 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()) + + "\""; + } + case FieldDescriptor::CPPTYPE_MESSAGE: + return "null"; + } +} + +string ProtoTypeName(const GeneratorOptions& options, + const FieldDescriptor* field) { + switch (field->type()) { + case FieldDescriptor::TYPE_BOOL: + return "bool"; + case FieldDescriptor::TYPE_INT32: + return "int32"; + case FieldDescriptor::TYPE_UINT32: + return "uint32"; + case FieldDescriptor::TYPE_SINT32: + return "sint32"; + case FieldDescriptor::TYPE_FIXED32: + return "fixed32"; + case FieldDescriptor::TYPE_SFIXED32: + return "sfixed32"; + case FieldDescriptor::TYPE_INT64: + return "int64"; + case FieldDescriptor::TYPE_UINT64: + return "uint64"; + case FieldDescriptor::TYPE_SINT64: + return "sint64"; + case FieldDescriptor::TYPE_FIXED64: + return "fixed64"; + case FieldDescriptor::TYPE_SFIXED64: + return "sfixed64"; + case FieldDescriptor::TYPE_FLOAT: + return "float"; + case FieldDescriptor::TYPE_DOUBLE: + return "double"; + case FieldDescriptor::TYPE_STRING: + return "string"; + case FieldDescriptor::TYPE_BYTES: + return "bytes"; + case FieldDescriptor::TYPE_GROUP: + return GetPath(options, field->message_type()); + case FieldDescriptor::TYPE_ENUM: + return GetPath(options, field->enum_type()); + case FieldDescriptor::TYPE_MESSAGE: + return GetPath(options, field->message_type()); + default: + return ""; + } +} + +string JSIntegerTypeName(const FieldDescriptor* field) { + return "number"; +} + +string JSTypeName(const GeneratorOptions& options, + const FieldDescriptor* field) { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_BOOL: + return "boolean"; + case FieldDescriptor::CPPTYPE_INT32: + return JSIntegerTypeName(field); + case FieldDescriptor::CPPTYPE_INT64: + return JSIntegerTypeName(field); + case FieldDescriptor::CPPTYPE_UINT32: + return JSIntegerTypeName(field); + case FieldDescriptor::CPPTYPE_UINT64: + return JSIntegerTypeName(field); + case FieldDescriptor::CPPTYPE_FLOAT: + return "number"; + case FieldDescriptor::CPPTYPE_DOUBLE: + return "number"; + case FieldDescriptor::CPPTYPE_STRING: + return "string"; + case FieldDescriptor::CPPTYPE_ENUM: + return GetPath(options, field->enum_type()); + case FieldDescriptor::CPPTYPE_MESSAGE: + return GetPath(options, field->message_type()); + default: + return ""; + } +} + +bool HasFieldPresence(const FieldDescriptor* field); + +string JSFieldTypeAnnotation(const GeneratorOptions& options, + const FieldDescriptor* field, + bool force_optional, + bool force_present, + bool singular_if_not_packed, + bool always_singular) { + bool is_primitive = + (field->cpp_type() != FieldDescriptor::CPPTYPE_ENUM && + field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE); + + string jstype = JSTypeName(options, field); + + if (field->is_repeated() && + !always_singular && + (field->is_packed() || !singular_if_not_packed)) { + if (!is_primitive) { + jstype = "!" + jstype; + } + jstype = "Array.<" + jstype + ">"; + if (!force_optional) { + jstype = "!" + jstype; + } + } + + if (field->is_optional() && is_primitive && + (!field->has_default_value() || force_optional) && !force_present) { + jstype += "?"; + } else if (field->is_required() && !is_primitive && !force_optional) { + jstype = "!" + jstype; + } + + if (force_optional && HasFieldPresence(field)) { + jstype += "|undefined"; + } + if (force_present && jstype[0] != '!' && !is_primitive) { + jstype = "!" + jstype; + } + + return jstype; +} + +string JSBinaryReaderMethodType(const FieldDescriptor* field) { + string name = field->type_name(); + if (name[0] >= 'a' && name[0] <= 'z') { + name[0] = (name[0] - 'a') + 'A'; + } + + return name; +} + +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()) { + name = "Repeated" + name; + } + return name; +} + +string JSBinaryReaderMethodName(const FieldDescriptor* field) { + return "read" + JSBinaryReadWriteMethodName(field, /* is_writer = */ false); +} + +string JSBinaryWriterMethodName(const FieldDescriptor* field) { + return "write" + JSBinaryReadWriteMethodName(field, /* is_writer = */ true); +} + +string JSReturnClause(const FieldDescriptor* desc) { + return ""; +} + +string JSReturnDoc(const GeneratorOptions& options, + const FieldDescriptor* desc) { + return ""; +} + +bool HasRepeatedFields(const Descriptor* desc) { + for (int i = 0; i < desc->field_count(); i++) { + if (desc->field(i)->is_repeated()) { + return true; + } + } + return false; +} + +static const char* kRepeatedFieldArrayName = ".repeatedFields_"; + +string RepeatedFieldsArrayName(const GeneratorOptions& options, + const Descriptor* desc) { + return HasRepeatedFields(desc) ? + (GetPath(options, desc) + kRepeatedFieldArrayName) : "null"; +} + +bool HasOneofFields(const Descriptor* desc) { + for (int i = 0; i < desc->field_count(); i++) { + if (desc->field(i)->containing_oneof()) { + return true; + } + } + return false; +} + +static const char* kOneofGroupArrayName = ".oneofGroups_"; + +string OneofFieldsArrayName(const GeneratorOptions& options, + const Descriptor* desc) { + return HasOneofFields(desc) ? + (GetPath(options, desc) + kOneofGroupArrayName) : "null"; +} + +string RepeatedFieldNumberList(const Descriptor* desc) { + std::vector<string> numbers; + for (int i = 0; i < desc->field_count(); i++) { + if (desc->field(i)->is_repeated()) { + numbers.push_back(JSFieldIndex(desc->field(i))); + } + } + return "[" + Join(numbers, ",") + "]"; +} + +string OneofGroupList(const Descriptor* desc) { + // List of arrays (one per oneof), each of which is a list of field indices + std::vector<string> oneof_entries; + for (int i = 0; i < desc->oneof_decl_count(); i++) { + const OneofDescriptor* oneof = desc->oneof_decl(i); + if (IgnoreOneof(oneof)) { + continue; + } + + std::vector<string> oneof_fields; + for (int j = 0; j < oneof->field_count(); j++) { + if (IgnoreField(oneof->field(j))) { + continue; + } + oneof_fields.push_back(JSFieldIndex(oneof->field(j))); + } + oneof_entries.push_back("[" + Join(oneof_fields, ",") + "]"); + } + return "[" + Join(oneof_entries, ",") + "]"; +} + +string JSOneofArray(const GeneratorOptions& options, + const FieldDescriptor* field) { + return OneofFieldsArrayName(options, field->containing_type()) + "[" + + JSOneofIndex(field->containing_oneof()) + "]"; +} + +string RelativeTypeName(const FieldDescriptor* field) { + assert(field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM || + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE); + // For a field with an enum or message type, compute a name relative to the + // path name of the message type containing this field. + string package = field->file()->package(); + string containing_type = field->containing_type()->full_name() + "."; + string type = (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) ? + field->enum_type()->full_name() : field->message_type()->full_name(); + + // |prefix| is advanced as we find separators '.' past the common package + // prefix that yield common prefixes in the containing type's name and this + // type's name. + int prefix = 0; + for (int i = 0; i < type.size() && i < containing_type.size(); i++) { + if (type[i] != containing_type[i]) { + break; + } + if (type[i] == '.' && i >= package.size()) { + prefix = i + 1; + } + } + + return type.substr(prefix); +} + +string JSExtensionsObjectName(const GeneratorOptions& options, + const Descriptor* desc) { + if (desc->full_name() == "google.protobuf.bridge.MessageSet") { + return "jspb.Message.messageSetExtensions"; + } else { + return GetPath(options, desc) + ".extensions"; + } +} + +string FieldDefinition(const GeneratorOptions& options, + const FieldDescriptor* field) { + string qualifier = field->is_repeated() ? "repeated" : + (field->is_optional() ? "optional" : "required"); + string type, name; + if (field->type() == FieldDescriptor::TYPE_ENUM || + field->type() == FieldDescriptor::TYPE_MESSAGE) { + type = RelativeTypeName(field); + name = field->name(); + } else if (field->type() == FieldDescriptor::TYPE_GROUP) { + type = "group"; + name = field->message_type()->name(); + } else { + type = ProtoTypeName(options, field); + name = field->name(); + } + return StringPrintf("%s %s %s = %d;", + qualifier.c_str(), + type.c_str(), + name.c_str(), + field->number()); +} + +string FieldComments(const FieldDescriptor* field) { + string comments; + if (field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL) { + comments += + " * Note that Boolean fields may be set to 0/1 when serialized from " + "a Java server.\n" + " * You should avoid comparisons like {@code val === true/false} in " + "those cases.\n"; + } + if (field->is_repeated()) { + comments += + " * If you change this array by adding, removing or replacing " + "elements, or if you\n" + " * replace the array itself, then you must call the setter to " + "update it.\n"; + } + return comments; +} + +bool ShouldGenerateExtension(const FieldDescriptor* field) { + return + field->is_extension() && + !IgnoreField(field); +} + +bool HasExtensions(const Descriptor* desc) { + if (desc->extension_count() > 0) { + return true; + } + for (int i = 0; i < desc->nested_type_count(); i++) { + if (HasExtensions(desc->nested_type(i))) { + return true; + } + } + return false; +} + +bool HasExtensions(const FileDescriptor* file) { + for (int i = 0; i < file->extension_count(); i++) { + if (ShouldGenerateExtension(file->extension(i))) { + return true; + } + } + for (int i = 0; i < file->message_type_count(); i++) { + if (HasExtensions(file->message_type(i))) { + return true; + } + } + return false; +} + +bool IsExtendable(const Descriptor* desc) { + return desc->extension_range_count() > 0; +} + +// Returns the max index in the underlying data storage array beyond which the +// extension object is used. +string GetPivot(const Descriptor* desc) { + static const int kDefaultPivot = (1 << 29); // max field number (29 bits) + + // Find the max field number + int max_field_number = 0; + for (int i = 0; i < desc->field_count(); i++) { + if (!IgnoreField(desc->field(i)) && + desc->field(i)->number() > max_field_number) { + max_field_number = desc->field(i)->number(); + } + } + + int pivot = -1; + if (IsExtendable(desc)) { + pivot = ((max_field_number + 1) < kDefaultPivot) ? + (max_field_number + 1) : kDefaultPivot; + } + + return SimpleItoa(pivot); +} + +// Returns true for fields that represent "null" as distinct from the default +// value. See https://go/proto3#heading=h.kozewqqcqhuz for more information. +bool HasFieldPresence(const FieldDescriptor* field) { + return + (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) || + (field->containing_oneof() != NULL) || + (field->file()->syntax() != FileDescriptor::SYNTAX_PROTO3); +} + +// For proto3 fields without presence, returns a string representing the default +// value in JavaScript. See https://go/proto3#heading=h.kozewqqcqhuz for more +// information. +string Proto3PrimitiveFieldDefault(const FieldDescriptor* field) { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + case FieldDescriptor::CPPTYPE_INT64: + case FieldDescriptor::CPPTYPE_UINT32: + case FieldDescriptor::CPPTYPE_UINT64: { + return "0"; + } + + case FieldDescriptor::CPPTYPE_ENUM: + case FieldDescriptor::CPPTYPE_FLOAT: + case FieldDescriptor::CPPTYPE_DOUBLE: + return "0"; + + case FieldDescriptor::CPPTYPE_BOOL: + return "false"; + + case FieldDescriptor::CPPTYPE_STRING: + return "\"\""; + + default: + // BYTES and MESSAGE are handled separately. + assert(false); + return ""; + } +} + +} // anonymous namespace + +void Generator::GenerateHeader(const GeneratorOptions& options, + io::Printer* printer) const { + printer->Print("/**\n" + " * @fileoverview\n" + " * @enhanceable\n" + " * @public\n" + " */\n" + "// GENERATED CODE -- DO NOT EDIT!\n" + "\n"); +} + +void Generator::FindProvides(const GeneratorOptions& options, + io::Printer* printer, + const vector<const FileDescriptor*>& files, + std::set<string>* provided) const { + for (int i = 0; i < files.size(); i++) { + for (int j = 0; j < files[i]->message_type_count(); j++) { + FindProvidesForMessage(options, printer, files[i]->message_type(j), + provided); + } + for (int j = 0; j < files[i]->enum_type_count(); j++) { + FindProvidesForEnum(options, printer, files[i]->enum_type(j), + provided); + } + } + + printer->Print("\n"); +} + +void Generator::FindProvidesForMessage( + const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc, + std::set<string>* provided) const { + string name = GetPath(options, desc); + provided->insert(name); + + for (int i = 0; i < desc->enum_type_count(); i++) { + FindProvidesForEnum(options, printer, desc->enum_type(i), + provided); + } + for (int i = 0; i < desc->nested_type_count(); i++) { + FindProvidesForMessage(options, printer, desc->nested_type(i), + provided); + } +} + +void Generator::FindProvidesForEnum(const GeneratorOptions& options, + io::Printer* printer, + const EnumDescriptor* enumdesc, + std::set<string>* provided) const { + string name = GetPath(options, enumdesc); + provided->insert(name); +} + +void Generator::FindProvidesForFields( + const GeneratorOptions& options, + io::Printer* printer, + const vector<const FieldDescriptor*>& fields, + std::set<string>* provided) const { + for (int i = 0; i < fields.size(); i++) { + const FieldDescriptor* field = fields[i]; + + if (IgnoreField(field)) { + continue; + } + + string name = + GetPath(options, field->file()) + "." + JSObjectFieldName(field); + provided->insert(name); + } +} + +void Generator::GenerateProvides(const GeneratorOptions& options, + io::Printer* printer, + std::set<string>* provided) const { + for (std::set<string>::iterator it = provided->begin(); + it != provided->end(); ++it) { + printer->Print("goog.provide('$name$');\n", + "name", *it); + } +} + +void Generator::GenerateRequires(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc, + std::set<string>* provided) const { + std::set<string> required; + std::set<string> forwards; + bool have_message = false; + FindRequiresForMessage(options, desc, + &required, &forwards, &have_message); + + GenerateRequiresImpl(options, printer, &required, &forwards, provided, + /* require_jspb = */ have_message, + /* require_extension = */ HasExtensions(desc)); +} + +void Generator::GenerateRequires(const GeneratorOptions& options, + io::Printer* printer, + const vector<const FileDescriptor*>& files, + std::set<string>* provided) const { + std::set<string> required; + std::set<string> 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; + } + } + + 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<const FieldDescriptor*>& fields, + std::set<string>* provided) const { + std::set<string> required; + std::set<string> forwards; + for (int i = 0; i < fields.size(); i++) { + const FieldDescriptor* field = fields[i]; + if (IgnoreField(field)) { + continue; + } + FindRequiresForExtension(options, field, &required, &forwards); + } + + GenerateRequiresImpl(options, printer, &required, &forwards, provided, + /* require_jspb = */ false, + /* require_extension = */ fields.size() > 0); +} + +void Generator::GenerateRequiresImpl(const GeneratorOptions& options, + io::Printer* printer, + std::set<string>* required, + std::set<string>* forwards, + std::set<string>* provided, + bool require_jspb, + bool require_extension) const { + if (require_jspb) { + printer->Print( + "goog.require('jspb.Message');\n"); + if (options.binary) { + printer->Print( + "goog.require('jspb.BinaryReader');\n" + "goog.require('jspb.BinaryWriter');\n"); + } + } + if (require_extension) { + printer->Print( + "goog.require('jspb.ExtensionFieldInfo');\n"); + } + + std::set<string>::iterator it; + for (it = required->begin(); it != required->end(); ++it) { + if (provided->find(*it) != provided->end()) { + continue; + } + printer->Print("goog.require('$name$');\n", + "name", *it); + } + + printer->Print("\n"); + + for (it = forwards->begin(); it != forwards->end(); ++it) { + if (provided->find(*it) != provided->end()) { + continue; + } + printer->Print("goog.forwardDeclare('$name$');\n", + "name", *it); + } +} + +bool NamespaceOnly(const Descriptor* desc) { + return false; +} + +void Generator::FindRequiresForMessage( + const GeneratorOptions& options, + const Descriptor* desc, + std::set<string>* required, + std::set<string>* forwards, + bool* have_message) const { + + + if (!NamespaceOnly(desc)) { + *have_message = true; + for (int i = 0; i < desc->field_count(); i++) { + const FieldDescriptor* field = desc->field(i); + if (IgnoreField(field)) { + continue; + } + FindRequiresForField(options, field, required, forwards); + } + } + + for (int i = 0; i < desc->extension_count(); i++) { + const FieldDescriptor* field = desc->extension(i); + if (IgnoreField(field)) { + continue; + } + FindRequiresForExtension(options, field, required, forwards); + } + + for (int i = 0; i < desc->nested_type_count(); i++) { + FindRequiresForMessage(options, desc->nested_type(i), required, forwards, + have_message); + } +} + +void Generator::FindRequiresForField(const GeneratorOptions& options, + const FieldDescriptor* field, + std::set<string>* required, + std::set<string>* forwards) const { + if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM && + // N.B.: file-level extensions with enum type do *not* create + // dependencies, as per original codegen. + !(field->is_extension() && field->extension_scope() == NULL)) { + if (options.add_require_for_enums) { + required->insert(GetPath(options, field->enum_type())); + } else { + forwards->insert(GetPath(options, field->enum_type())); + } + } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + required->insert(GetPath(options, field->message_type())); + } +} + +void Generator::FindRequiresForExtension(const GeneratorOptions& options, + const FieldDescriptor* field, + std::set<string>* required, + std::set<string>* forwards) const { + if (field->containing_type()->full_name() != "google.protobuf.bridge.MessageSet") { + required->insert(GetPath(options, field->containing_type())); + } + FindRequiresForField(options, field, required, forwards); +} + +void Generator::GenerateTestOnly(const GeneratorOptions& options, + io::Printer* printer) const { + if (options.testonly) { + printer->Print("goog.setTestOnly();\n\n"); + } + printer->Print("\n"); +} + +void Generator::GenerateClassesAndEnums(const GeneratorOptions& options, + io::Printer* printer, + const FileDescriptor* file) const { + for (int i = 0; i < file->message_type_count(); i++) { + GenerateClass(options, printer, file->message_type(i)); + } + for (int i = 0; i < file->enum_type_count(); i++) { + GenerateEnum(options, printer, file->enum_type(i)); + } +} + +void Generator::GenerateClass(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const { + if (!NamespaceOnly(desc)) { + printer->Print("\n"); + GenerateClassConstructor(options, printer, desc); + GenerateClassFieldInfo(options, printer, desc); + + + GenerateClassToObject(options, printer, desc); + if (options.binary) { + // These must come *before* the extension-field info generation in + // GenerateClassRegistration so that references to the binary + // serialization/deserialization functions may be placed in the extension + // objects. + GenerateClassDeserializeBinary(options, printer, desc); + GenerateClassSerializeBinary(options, printer, desc); + } + GenerateClassClone(options, printer, desc); + GenerateClassRegistration(options, printer, desc); + GenerateClassFields(options, printer, desc); + if (IsExtendable(desc) && desc->full_name() != "google.protobuf.bridge.MessageSet") { + GenerateClassExtensionFieldInfo(options, printer, desc); + } + } + + // Recurse on nested types. + for (int i = 0; i < desc->enum_type_count(); i++) { + GenerateEnum(options, printer, desc->enum_type(i)); + } + for (int i = 0; i < desc->nested_type_count(); i++) { + GenerateClass(options, printer, desc->nested_type(i)); + } +} + +void Generator::GenerateClassConstructor(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const { + printer->Print( + "/**\n" + " * Generated by JsPbCodeGenerator.\n" + " * @param {Array=} opt_data Optional initial data array, typically " + "from a\n" + " * server response, or constructed directly in Javascript. The array " + "is used\n" + " * in place and becomes part of the constructed object. It is not " + "cloned.\n" + " * If no data is provided, the constructed object will be empty, but " + "still\n" + " * valid.\n" + " * @extends {jspb.Message}\n" + " * @constructor\n" + " */\n" + "$classname$ = function(opt_data) {\n", + "classname", GetPath(options, desc)); + string message_id = GetMessageId(desc); + printer->Print( + " jspb.Message.initialize(this, opt_data, $messageId$, $pivot$, " + "$rptfields$, $oneoffields$);\n", + "messageId", !message_id.empty() ? + ("'" + message_id + "'") : + (IsResponse(desc) ? "''" : "0"), + "pivot", GetPivot(desc), + "rptfields", RepeatedFieldsArrayName(options, desc), + "oneoffields", OneofFieldsArrayName(options, desc)); + printer->Print( + "};\n" + "goog.inherits($classname$, jspb.Message);\n" + "if (goog.DEBUG && !COMPILED) {\n" + " $classname$.displayName = '$classname$';\n" + "}\n", + "classname", GetPath(options, desc)); +} + +void Generator::GenerateClassFieldInfo(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const { + if (HasRepeatedFields(desc)) { + printer->Print( + "/**\n" + " * List of repeated fields within this message type.\n" + " * @private {!Array<number>}\n" + " * @const\n" + " */\n" + "$classname$$rptfieldarray$ = $rptfields$;\n" + "\n", + "classname", GetPath(options, desc), + "rptfieldarray", kRepeatedFieldArrayName, + "rptfields", RepeatedFieldNumberList(desc)); + } + + if (HasOneofFields(desc)) { + printer->Print( + "/**\n" + " * Oneof group definitions for this message. Each group defines the " + "field\n" + " * numbers belonging to that group. When of these fields' value is " + "set, all\n" + " * other fields in the group are cleared. During deserialization, if " + "multiple\n" + " * fields are encountered for a group, only the last value seen will " + "be kept.\n" + " * @private {!Array<!Array<number>>}\n" + " * @const\n" + " */\n" + "$classname$$oneofgrouparray$ = $oneofgroups$;\n" + "\n", + "classname", GetPath(options, desc), + "oneofgrouparray", kOneofGroupArrayName, + "oneofgroups", OneofGroupList(desc)); + + for (int i = 0; i < desc->oneof_decl_count(); i++) { + if (IgnoreOneof(desc->oneof_decl(i))) { + continue; + } + GenerateOneofCaseDefinition(options, printer, desc->oneof_decl(i)); + } + } +} + +void Generator::GenerateClassXid(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const { + printer->Print( + "\n" + "\n" + "$class$.prototype.messageXid = xid('$class$');\n", + "class", GetPath(options, desc)); +} + +void Generator::GenerateOneofCaseDefinition( + const GeneratorOptions& options, + io::Printer* printer, + const OneofDescriptor* oneof) const { + printer->Print( + "/**\n" + " * @enum {number}\n" + " */\n" + "$classname$.$oneof$Case = {\n" + " $upcase$_NOT_SET: 0", + "classname", GetPath(options, oneof->containing_type()), + "oneof", JSOneofName(oneof), + "upcase", ToEnumCase(oneof->name())); + + for (int i = 0; i < oneof->field_count(); i++) { + if (IgnoreField(oneof->field(i))) { + continue; + } + + printer->Print( + ",\n" + " $upcase$: $number$", + "upcase", ToEnumCase(oneof->field(i)->name()), + "number", JSFieldIndex(oneof->field(i))); + } + + printer->Print( + "\n" + "};\n" + "\n" + "/**\n" + " * @return {$class$.$oneof$Case}\n" + " */\n" + "$class$.prototype.get$oneof$Case = function() {\n" + " return /** @type {$class$.$oneof$Case} */(jspb.Message." + "computeOneofCase(this, $class$.oneofGroups_[$oneofindex$]));\n" + "};\n" + "\n", + "class", GetPath(options, oneof->containing_type()), + "oneof", JSOneofName(oneof), + "oneofindex", JSOneofIndex(oneof)); +} + +void Generator::GenerateClassToObject(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const { + printer->Print( + "\n" + "\n" + "if (jspb.Message.GENERATE_TO_OBJECT) {\n" + "/**\n" + " * Creates an object representation of this proto suitable for use in " + "Soy templates.\n" + " * Field names that are reserved in JavaScript and will be renamed to " + "pb_name.\n" + " * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.\n" + " * For the list of reserved names please see:\n" + " * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.\n" + " * @param {boolean=} opt_includeInstance Whether to include the JSPB " + "instance\n" + " * for transitional soy proto support: http://goto/soy-param-" + "migration\n" + " * @return {!Object}\n" + " */\n" + "$classname$.prototype.toObject = function(opt_includeInstance) {\n" + " return $classname$.toObject(opt_includeInstance, this);\n" + "};\n" + "\n" + "\n" + "/**\n" + " * Static version of the {@see toObject} method.\n" + " * @param {boolean|undefined} includeInstance Whether to include the " + "JSPB\n" + " * instance for transitional soy proto support:\n" + " * http://goto/soy-param-migration\n" + " * @param {!$classname$} msg The msg instance to transform.\n" + " * @return {!Object}\n" + " */\n" + "$classname$.toObject = function(includeInstance, msg) {\n" + " var f, obj = {", + "classname", GetPath(options, desc)); + + bool first = true; + for (int i = 0; i < desc->field_count(); i++) { + const FieldDescriptor* field = desc->field(i); + if (IgnoreField(field)) { + continue; + } + + if (!first) { + printer->Print(",\n "); + } else { + printer->Print("\n "); + first = false; + } + + GenerateClassFieldToObject(options, printer, field); + } + + if (!first) { + printer->Print("\n };\n\n"); + } else { + printer->Print("\n\n };\n\n"); + } + + if (IsExtendable(desc)) { + printer->Print( + " jspb.Message.toObjectExtension(/** @type {!jspb.Message} */ (msg), " + "obj,\n" + " $extObject$, $class$.prototype.getExtension,\n" + " includeInstance);\n", + "extObject", JSExtensionsObjectName(options, desc), + "class", GetPath(options, desc)); + } + + printer->Print( + " if (includeInstance) {\n" + " obj.$$jspbMessageInstance = msg\n" + " }\n" + " return obj;\n" + "};\n" + "}\n" + "\n" + "\n", + "classname", GetPath(options, desc)); +} + +void Generator::GenerateClassFieldToObject(const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* field) const { + printer->Print("$fieldname$: ", + "fieldname", JSObjectFieldName(field)); + + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + // Message field. + if (field->is_repeated()) { + { + printer->Print("jspb.Message.toObjectList(msg.get$getter$(),\n" + " $type$.toObject, includeInstance)", + "getter", JSGetterName(field), + "type", GetPath(options, field->message_type())); + } + } else { + printer->Print("(f = msg.get$getter$()) && " + "$type$.toObject(includeInstance, f)", + "getter", JSGetterName(field), + "type", GetPath(options, field->message_type())); + } + } else { + // Simple field (singular or repeated). + if (!HasFieldPresence(field) && !field->is_repeated()) { + // Delegate to the generated get<field>() method in order not to duplicate + // the proto3-field-default-value logic here. + printer->Print("msg.get$getter$()", + "getter", JSGetterName(field)); + } else { + if (field->has_default_value()) { + printer->Print("jspb.Message.getField(msg, $index$) != null ? " + "jspb.Message.getField(msg, $index$) : $defaultValue$", + "index", JSFieldIndex(field), + "defaultValue", JSFieldDefault(field)); + } else { + printer->Print("jspb.Message.getField(msg, $index$)", + "index", JSFieldIndex(field)); + } + } + } +} + +void Generator::GenerateClassFromObject(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const { + printer->Print( + "if (jspb.Message.GENERATE_FROM_OBJECT) {\n" + "/**\n" + " * Loads data from an object into a new instance of this proto.\n" + " * @param {!Object} obj The object representation of this proto to\n" + " * load the data from.\n" + " * @return {!$classname$}\n" + " */\n" + "$classname$.fromObject = function(obj) {\n" + " var f, msg = new $classname$();\n", + "classname", GetPath(options, desc)); + + for (int i = 0; i < desc->field_count(); i++) { + const FieldDescriptor* field = desc->field(i); + GenerateClassFieldFromObject(options, printer, field); + } + + printer->Print( + " return msg;\n" + "};\n" + "}\n"); +} + +void Generator::GenerateClassFieldFromObject( + const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* field) const { + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + // Message field (singular or repeated) + if (field->is_repeated()) { + { + printer->Print( + " goog.isDef(obj.$name$) && " + "jspb.Message.setRepeatedWrapperField(\n" + " msg, $index$, goog.array.map(obj.$name$, function(i) {\n" + " return $fieldclass$.fromObject(i);\n" + " }));\n", + "name", JSObjectFieldName(field), + "index", JSFieldIndex(field), + "fieldclass", GetPath(options, field->message_type())); + } + } else { + printer->Print( + " goog.isDef(obj.$name$) && jspb.Message.setWrapperField(\n" + " msg, $index$, $fieldclass$.fromObject(obj.$name$));\n", + "name", JSObjectFieldName(field), + "index", JSFieldIndex(field), + "fieldclass", GetPath(options, field->message_type())); + } + } else { + // Simple (primitive) field. + printer->Print( + " goog.isDef(obj.$name$) && jspb.Message.setField(msg, $index$, " + "obj.$name$);\n", + "name", JSObjectFieldName(field), + "index", JSFieldIndex(field)); + } +} + +void Generator::GenerateClassClone(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const { + printer->Print( + "/**\n" + " * Creates a deep clone of this proto. No data is shared with the " + "original.\n" + " * @return {!$name$} The clone.\n" + " */\n" + "$name$.prototype.cloneMessage = function() {\n" + " return /** @type {!$name$} */ (jspb.Message.cloneMessage(this));\n" + "};\n\n\n", + "name", GetPath(options, desc)); +} + +void Generator::GenerateClassRegistration(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const { + // Register any extensions defined inside this message type. + for (int i = 0; i < desc->extension_count(); i++) { + const FieldDescriptor* extension = desc->extension(i); + if (ShouldGenerateExtension(extension)) { + GenerateExtension(options, printer, extension); + } + } + +} + +void Generator::GenerateClassFields(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const { + for (int i = 0; i < desc->field_count(); i++) { + if (!IgnoreField(desc->field(i))) { + GenerateClassField(options, printer, desc->field(i)); + } + } +} + +void Generator::GenerateClassField(const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* field) const { + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + printer->Print( + "/**\n" + " * $fielddef$\n" + "$comment$" + " * @return {$type$}\n" + " */\n", + "fielddef", FieldDefinition(options, field), + "comment", FieldComments(field), + "type", JSFieldTypeAnnotation(options, field, + /* force_optional = */ false, + /* force_present = */ false, + /* singular_if_not_packed = */ false, + /* always_singular = */ false)); + printer->Print( + "$class$.prototype.get$name$ = function() {\n" + " return /** @type{$type$} */ (\n" + " jspb.Message.get$rpt$WrapperField(this, $wrapperclass$, " + "$index$$required$));\n" + "};\n" + "\n" + "\n", + "class", GetPath(options, field->containing_type()), + "name", JSGetterName(field), + "type", JSFieldTypeAnnotation(options, field, + /* force_optional = */ false, + /* force_present = */ false, + /* singular_if_not_packed = */ false, + /* always_singular = */ false), + "rpt", (field->is_repeated() ? "Repeated" : ""), + "index", JSFieldIndex(field), + "wrapperclass", GetPath(options, field->message_type()), + "required", (field->label() == FieldDescriptor::LABEL_REQUIRED ? + ", 1" : "")); + printer->Print( + "/** @param {$optionaltype$} value $returndoc$ */\n" + "$class$.prototype.set$name$ = function(value) {\n" + " jspb.Message.set$oneoftag$$repeatedtag$WrapperField(", + "optionaltype", + JSFieldTypeAnnotation(options, field, + /* force_optional = */ true, + /* force_present = */ false, + /* singular_if_not_packed = */ false, + /* always_singular = */ false), + "returndoc", JSReturnDoc(options, field), + "class", GetPath(options, field->containing_type()), + "name", JSGetterName(field), + "oneoftag", (field->containing_oneof() ? "Oneof" : ""), + "repeatedtag", (field->is_repeated() ? "Repeated" : "")); + + printer->Print( + "this, $index$$oneofgroup$, value);$returnvalue$\n" + "};\n" + "\n" + "\n", + "index", JSFieldIndex(field), + "oneofgroup", (field->containing_oneof() ? + (", " + JSOneofArray(options, field)) : ""), + "returnvalue", JSReturnClause(field)); + + printer->Print( + "$class$.prototype.clear$name$ = function() {\n" + " this.set$name$($clearedvalue$);$returnvalue$\n" + "};\n" + "\n" + "\n", + "class", GetPath(options, field->containing_type()), + "name", JSGetterName(field), + "clearedvalue", (field->is_repeated() ? "[]" : "undefined"), + "returnvalue", JSReturnClause(field)); + + } else { + string typed_annotation; + + // Simple (primitive) field, either singular or repeated. + { + typed_annotation = JSFieldTypeAnnotation(options, field, + /* force_optional = */ false, + /* force_present = */ !HasFieldPresence(field), + /* singular_if_not_packed = */ false, + /* always_singular = */ false), + printer->Print( + "/**\n" + " * $fielddef$\n" + "$comment$" + " * @return {$type$}\n" + " */\n", + "fielddef", FieldDefinition(options, field), + "comment", FieldComments(field), + "type", typed_annotation); + } + + printer->Print( + "$class$.prototype.get$name$ = function() {\n", + "class", GetPath(options, field->containing_type()), + "name", JSGetterName(field)); + + { + printer->Print( + " return /** @type {$type$} */ (", + "type", typed_annotation); + } + + // For proto3 fields without presence, use special getters that will return + // defaults when the field is unset, possibly constructing a value if + // required. + if (!HasFieldPresence(field) && !field->is_repeated()) { + printer->Print("jspb.Message.getFieldProto3(this, $index$, $default$)", + "index", JSFieldIndex(field), + "default", Proto3PrimitiveFieldDefault(field)); + } else { + if (field->has_default_value()) { + printer->Print("jspb.Message.getField(this, $index$) != null ? " + "jspb.Message.getField(this, $index$) : $defaultValue$", + "index", JSFieldIndex(field), + "defaultValue", JSFieldDefault(field)); + } else { + printer->Print("jspb.Message.getField(this, $index$)", + "index", JSFieldIndex(field)); + } + } + + { + printer->Print( + ");\n" + "};\n" + "\n" + "\n"); + } + + { + 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), + "returndoc", JSReturnDoc(options, field)); + } + + printer->Print( + "$class$.prototype.set$name$ = function(value) {\n" + " jspb.Message.set$oneoftag$Field(this, $index$", + "class", GetPath(options, field->containing_type()), + "name", JSGetterName(field), + "oneoftag", (field->containing_oneof() ? "Oneof" : ""), + "index", JSFieldIndex(field)); + printer->Print( + "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);$returnvalue$\n" + "};\n" + "\n" + "\n", + "type", "", + "typeclose", "", + "oneofgroup", + (field->containing_oneof() ? (", " + JSOneofArray(options, field)) + : ""), + "returnvalue", JSReturnClause(field), "rptvalueinit", + (field->is_repeated() ? " || []" : "")); + + + if (HasFieldPresence(field)) { + printer->Print( + "$class$.prototype.clear$name$ = function() {\n" + " jspb.Message.set$oneoftag$Field(this, $index$$oneofgroup$, ", + "class", GetPath(options, field->containing_type()), + "name", JSGetterName(field), + "oneoftag", (field->containing_oneof() ? "Oneof" : ""), + "oneofgroup", (field->containing_oneof() ? + (", " + JSOneofArray(options, field)) : ""), + "index", JSFieldIndex(field)); + printer->Print( + "$clearedvalue$);$returnvalue$\n" + "};\n" + "\n" + "\n", + "clearedvalue", (field->is_repeated() ? "[]" : "undefined"), + "returnvalue", JSReturnClause(field)); + } + } +} + +void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const { + if (IsExtendable(desc)) { + printer->Print( + "\n" + "/**\n" + " * The extensions registered with this message class. This is a " + "map of\n" + " * extension field number to fieldInfo object.\n" + " *\n" + " * For example:\n" + " * { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, " + "ctor: proto.example.MyMessage} }\n" + " *\n" + " * fieldName contains the JsCompiler renamed field name property " + "so that it\n" + " * works in OPTIMIZED mode.\n" + " *\n" + " * @type {!Object.<number, jspb.ExtensionFieldInfo>}\n" + " */\n" + "$class$.extensions = {};\n" + "\n", + "class", GetPath(options, desc)); + } +} + + +void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const { + // TODO(cfallin): Handle lazy decoding when requested by field option and/or + // by default for 'bytes' fields and packed repeated fields. + + printer->Print( + "/**\n" + " * Deserializes binary data (in protobuf wire format).\n" + " * @param {jspb.ByteSource} bytes The bytes to deserialize.\n" + " * @return {!$class$}\n" + " */\n" + "$class$.deserializeBinary = function(bytes) {\n" + " var reader = new jspb.BinaryReader(bytes);\n" + " var msg = new $class$;\n" + " return $class$.deserializeBinaryFromReader(msg, reader);\n" + "};\n" + "\n" + "\n" + "/**\n" + " * Deserializes binary data (in protobuf wire format) from the\n" + " * given reader into the given message object.\n" + " * @param {!$class$} msg The message object to deserialize into.\n" + " * @param {!jspb.BinaryReader} reader The BinaryReader to use.\n" + " * @return {!$class$}\n" + " */\n" + "$class$.deserializeBinaryFromReader = function(msg, reader) {\n" + " while (reader.nextField()) {\n" + " if (reader.isEndGroup()) {\n" + " break;\n" + " }\n" + " var field = reader.getFieldNumber();\n" + " switch (field) {\n", + "class", GetPath(options, desc)); + + for (int i = 0; i < desc->field_count(); i++) { + GenerateClassDeserializeBinaryField(options, printer, desc->field(i)); + } + + printer->Print( + " default:\n"); + if (IsExtendable(desc)) { + printer->Print( + " jspb.Message.readBinaryExtension(msg, reader, $extobj$,\n" + " $class$.prototype.getExtension,\n" + " $class$.prototype.setExtension);\n" + " break;\n", + "extobj", JSExtensionsObjectName(options, desc), + "class", GetPath(options, desc)); + } else { + printer->Print( + " reader.skipField();\n" + " break;\n"); + } + + printer->Print( + " }\n" + " }\n" + " return msg;\n" + "};\n" + "\n" + "\n"); +} + +void Generator::GenerateClassDeserializeBinaryField( + const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* field) const { + + printer->Print(" case $num$:\n", + "num", SimpleItoa(field->number())); + + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + printer->Print( + " var value = new $fieldclass$;\n" + " reader.read$msgOrGroup$($grpfield$value," + "$fieldclass$.deserializeBinaryFromReader);\n", + "fieldclass", GetPath(options, field->message_type()), + "msgOrGroup", (field->type() == FieldDescriptor::TYPE_GROUP) ? + "Group" : "Message", + "grpfield", (field->type() == FieldDescriptor::TYPE_GROUP) ? + (SimpleItoa(field->number()) + ", ") : ""); + } else { + printer->Print( + " var value = /** @type {$fieldtype$} */ (reader.$reader$());\n", + "fieldtype", JSFieldTypeAnnotation(options, field, false, true, + /* singular_if_not_packed = */ true, + /* always_singular = */ false), + "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)); + } 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 + // the field's value directly. + printer->Print( + " msg.set$name$(value);\n", + "name", JSGetterName(field)); + } + + printer->Print(" break;\n"); +} + +void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const { + printer->Print( + "/**\n" + " * Class method variant: serializes the given message to binary data\n" + " * (in protobuf wire format), writing to the given BinaryWriter.\n" + " * @param {!$class$} message\n" + " * @param {!jspb.BinaryWriter} writer\n" + " */\n" + "$class$.serializeBinaryToWriter = function(message, " + "writer) {\n" + " message.serializeBinaryToWriter(writer);\n" + "};\n" + "\n" + "\n" + "/**\n" + " * Serializes the message to binary data (in protobuf wire format).\n" + " * @return {!Uint8Array}\n" + " */\n" + "$class$.prototype.serializeBinary = function() {\n" + " var writer = new jspb.BinaryWriter();\n" + " this.serializeBinaryToWriter(writer);\n" + " return writer.getResultBuffer();\n" + "};\n" + "\n" + "\n" + "/**\n" + " * Serializes the message to binary data (in protobuf wire format),\n" + " * writing to the given BinaryWriter.\n" + " * @param {!jspb.BinaryWriter} writer\n" + " */\n" + "$class$.prototype.serializeBinaryToWriter = function (writer) {\n" + " var f = undefined;\n", + "class", GetPath(options, desc)); + + for (int i = 0; i < desc->field_count(); i++) { + GenerateClassSerializeBinaryField(options, printer, desc->field(i)); + } + + if (IsExtendable(desc)) { + printer->Print( + " jspb.Message.serializeBinaryExtensions(this, writer, $extobj$,\n" + " $class$.prototype.getExtension);\n", + "extobj", JSExtensionsObjectName(options, desc), + "class", GetPath(options, desc)); + } + + printer->Print( + "};\n" + "\n" + "\n"); +} + +void Generator::GenerateClassSerializeBinaryField( + const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* field) const { + printer->Print( + " f = this.get$name$();\n", + "name", JSGetterName(field)); + + if (field->is_repeated()) { + printer->Print( + " if (f.length > 0) {\n"); + } else { + if (HasFieldPresence(field)) { + printer->Print( + " if (f != null) {\n"); + } else { + // No field presence: serialize onto the wire only if value is + // non-default. Defaults are documented here: + // https://goto.google.com/lhdfm + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + case FieldDescriptor::CPPTYPE_INT64: + case FieldDescriptor::CPPTYPE_UINT32: + case FieldDescriptor::CPPTYPE_UINT64: { + { + printer->Print(" if (f !== 0) {\n"); + } + break; + } + + case FieldDescriptor::CPPTYPE_ENUM: + case FieldDescriptor::CPPTYPE_FLOAT: + case FieldDescriptor::CPPTYPE_DOUBLE: + printer->Print( + " if (f !== 0.0) {\n"); + break; + case FieldDescriptor::CPPTYPE_BOOL: + printer->Print( + " if (f) {\n"); + break; + case FieldDescriptor::CPPTYPE_STRING: + printer->Print( + " if (f.length > 0) {\n"); + break; + default: + assert(false); + break; + } + } + } + + printer->Print( + " writer.$writer$(\n" + " $index$,\n" + " f", + "writer", JSBinaryWriterMethodName(field), + "name", JSGetterName(field), + "index", SimpleItoa(field->number())); + + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + printer->Print( + ",\n" + " $submsg$.serializeBinaryToWriter\n", + "submsg", GetPath(options, field->message_type())); + } else { + printer->Print("\n"); + } + printer->Print( + " );\n" + " }\n"); +} + +void Generator::GenerateEnum(const GeneratorOptions& options, + io::Printer* printer, + const EnumDescriptor* enumdesc) const { + printer->Print( + "/**\n" + " * @enum {number}\n" + " */\n" + "$name$ = {\n", + "name", GetPath(options, enumdesc)); + + for (int i = 0; i < enumdesc->value_count(); i++) { + const EnumValueDescriptor* value = enumdesc->value(i); + printer->Print( + " $name$: $value$$comma$\n", + "name", ToEnumCase(value->name()), + "value", SimpleItoa(value->number()), + "comma", (i == enumdesc->value_count() - 1) ? "" : ","); + } + + printer->Print( + "};\n" + "\n"); +} + +void Generator::GenerateExtension(const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* field) const { + string extension_scope = + (field->extension_scope() ? + GetPath(options, field->extension_scope()) : + GetPath(options, field->file())); + + printer->Print( + "\n" + "/**\n" + " * A tuple of {field number, class constructor} for the extension\n" + " * field named `$name$`.\n" + " * @type {!jspb.ExtensionFieldInfo.<$extensionType$>}\n" + " */\n" + "$class$.$name$ = new jspb.ExtensionFieldInfo(\n", + "name", JSObjectFieldName(field), + "class", extension_scope, + "extensionType", JSFieldTypeAnnotation( + options, field, + /* force_optional = */ false, + /* force_present = */ true, + /* singular_if_not_packed = */ false, + /* always_singular = */ false)); + printer->Print( + " $index$,\n" + " {$name$: 0},\n" + " $ctor$,\n" + " /** @type {?function((boolean|undefined),!jspb.Message=): " + "!Object} */ (\n" + " $toObject$),\n" + " $repeated$", + "index", SimpleItoa(field->number()), + "name", JSObjectFieldName(field), + "ctor", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ? + GetPath(options, field->message_type()) : string("null")), + "toObject", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ? + (GetPath(options, field->message_type()) + ".toObject") : + string("null")), + "repeated", (field->is_repeated() ? "1" : "0")); + + if (options.binary) { + printer->Print( + ",\n" + " jspb.BinaryReader.prototype.$binaryReaderFn$,\n" + " jspb.BinaryWriter.prototype.$binaryWriterFn$,\n" + " $binaryMessageSerializeFn$,\n" + " $binaryMessageDeserializeFn$,\n" + " $isPacked$);\n", + "binaryReaderFn", JSBinaryReaderMethodName(field), + "binaryWriterFn", JSBinaryWriterMethodName(field), + "binaryMessageSerializeFn", + (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ? + (GetPath(options, field->message_type()) + + ".serializeBinaryToWriter") : "null", + "binaryMessageDeserializeFn", + (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ? + (GetPath(options, field->message_type()) + + ".deserializeBinaryFromReader") : "null", + "isPacked", (field->is_packed() ? "true" : "false")); + } else { + printer->Print(");\n"); + } + + printer->Print( + "// This registers the extension field with the extended class, so that\n" + "// toObject() will function correctly.\n" + "$extendName$[$index$] = $class$.$name$;\n" + "\n", + "extendName", JSExtensionsObjectName(options, field->containing_type()), + "index", SimpleItoa(field->number()), + "class", extension_scope, + "name", JSObjectFieldName(field)); +} + +bool GeneratorOptions::ParseFromOptions( + const vector< pair< string, string > >& options, + string* error) { + for (int i = 0; i < options.size(); i++) { + if (options[i].first == "add_require_for_enums") { + if (options[i].second != "") { + *error = "Unexpected option value for add_require_for_enums"; + return false; + } + add_require_for_enums = true; + } else if (options[i].first == "binary") { + if (options[i].second != "") { + *error = "Unexpected option value for binary"; + return false; + } + binary = true; + } else if (options[i].first == "testonly") { + if (options[i].second != "") { + *error = "Unexpected option value for testonly"; + return false; + } + testonly = true; + } else if (options[i].first == "error_on_name_conflict") { + if (options[i].second != "") { + *error = "Unexpected option value for error_on_name_conflict"; + return false; + } + error_on_name_conflict = true; + } else if (options[i].first == "output_dir") { + output_dir = options[i].second; + } else if (options[i].first == "namespace_prefix") { + namespace_prefix = options[i].second; + } else if (options[i].first == "library") { + library = options[i].second; + } else { + // Assume any other option is an output directory, as long as it is a bare + // `key` rather than a `key=value` option. + if (options[i].second != "") { + *error = "Unknown option: " + options[i].first; + return false; + } + output_dir = options[i].first; + } + } + + return true; +} + +void Generator::GenerateFilesInDepOrder( + const GeneratorOptions& options, + io::Printer* printer, + const vector<const FileDescriptor*>& files) const { + // Build a std::set over all files so that the DFS can detect when it recurses + // into a dep not specified in the user's command line. + std::set<const FileDescriptor*> all_files(files.begin(), files.end()); + // Track the in-progress set of files that have been generated already. + std::set<const FileDescriptor*> generated; + for (int i = 0; i < files.size(); i++) { + GenerateFileAndDeps(options, printer, files[i], &all_files, &generated); + } +} + +void Generator::GenerateFileAndDeps( + const GeneratorOptions& options, + io::Printer* printer, + const FileDescriptor* root, + std::set<const FileDescriptor*>* all_files, + std::set<const FileDescriptor*>* generated) const { + // Skip if already generated. + if (generated->find(root) != generated->end()) { + return; + } + generated->insert(root); + + // Generate all dependencies before this file's content. + for (int i = 0; i < root->dependency_count(); i++) { + const FileDescriptor* dep = root->dependency(i); + GenerateFileAndDeps(options, printer, dep, all_files, generated); + } + + // Generate this file's content. Only generate if the file is part of the + // original set requested to be generated; i.e., don't take all transitive + // deps down to the roots. + if (all_files->find(root) != all_files->end()) { + GenerateClassesAndEnums(options, printer, root); + } +} + +bool Generator::GenerateAll(const vector<const FileDescriptor*>& files, + const string& parameter, + GeneratorContext* context, + string* error) const { + vector< pair< string, string > > option_pairs; + ParseGeneratorParameter(parameter, &option_pairs); + GeneratorOptions options; + if (!options.ParseFromOptions(option_pairs, error)) { + return false; + } + + + // We're either generating a single library file with definitions for message + // and enum types in *all* FileDescriptor inputs, or we're generating a single + // file for each type. + if (options.library != "") { + string filename = options.output_dir + "/" + options.library + ".js"; + google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename)); + GOOGLE_CHECK(output.get()); + io::Printer printer(output.get(), '$'); + + // Pull out all extensions -- we need these to generate all + // provides/requires. + vector<const FieldDescriptor*> extensions; + for (int i = 0; i < files.size(); i++) { + for (int j = 0; j < files[i]->extension_count(); j++) { + const FieldDescriptor* extension = files[i]->extension(j); + extensions.push_back(extension); + } + } + + GenerateHeader(options, &printer); + + std::set<string> provided; + FindProvides(options, &printer, files, &provided); + FindProvidesForFields(options, &printer, extensions, &provided); + GenerateProvides(options, &printer, &provided); + GenerateTestOnly(options, &printer); + GenerateRequires(options, &printer, files, &provided); + + GenerateFilesInDepOrder(options, &printer, files); + + for (int i = 0; i < extensions.size(); i++) { + if (ShouldGenerateExtension(extensions[i])) { + GenerateExtension(options, &printer, extensions[i]); + } + } + + if (printer.failed()) { + return false; + } + } else { + // Collect all types, and print each type to a separate file. Pull out + // free-floating extensions while we make this pass. + map< string, vector<const FieldDescriptor*> > 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<string, const void*> desc_by_filename; + // Set of descriptors allowed to generate files. + set<const void*> allowed_descs; + + for (int i = 0; i < files.size(); i++) { + // Collect all (descriptor, filename) pairs. + map<const void*, string> 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<const void*, string>::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); + } + } + + // 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()) { + continue; + } + + string filename = options.output_dir + "/" + + ToFileName(desc->name()) + ".js"; + google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( + context->Open(filename)); + GOOGLE_CHECK(output.get()); + io::Printer printer(output.get(), '$'); + + GenerateHeader(options, &printer); + + std::set<string> provided; + FindProvidesForMessage(options, &printer, desc, &provided); + GenerateProvides(options, &printer, &provided); + GenerateTestOnly(options, &printer); + GenerateRequires(options, &printer, desc, &provided); + + GenerateClass(options, &printer, desc); + + if (printer.failed()) { + return false; + } + } + 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()) { + continue; + } + + string filename = options.output_dir + "/" + + ToFileName(enumdesc->name()) + ".js"; + + google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( + context->Open(filename)); + GOOGLE_CHECK(output.get()); + io::Printer printer(output.get(), '$'); + + GenerateHeader(options, &printer); + + std::set<string> provided; + FindProvidesForEnum(options, &printer, enumdesc, &provided); + GenerateProvides(options, &printer, &provided); + GenerateTestOnly(options, &printer); + + GenerateEnum(options, &printer, enumdesc); + + if (printer.failed()) { + 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); + } + } + + // Generate extensions in separate files. + map< string, vector<const FieldDescriptor*> >::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<io::ZeroCopyOutputStream> output( + context->Open(filename)); + GOOGLE_CHECK(output.get()); + io::Printer printer(output.get(), '$'); + + GenerateHeader(options, &printer); + + std::set<string> 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 < it->second.size(); j++) { + if (ShouldGenerateExtension(it->second[j])) { + GenerateExtension(options, &printer, it->second[j]); + } + } + } + } + + return true; +} + +} // namespace js +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/js/js_generator.h b/src/google/protobuf/compiler/js/js_generator.h new file mode 100755 index 00000000..db2dceb3 --- /dev/null +++ b/src/google/protobuf/compiler/js/js_generator.h @@ -0,0 +1,265 @@ +// 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_JS_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_JS_GENERATOR_H__ + +#include <string> +#include <set> + +#include <google/protobuf/compiler/code_generator.h> + +namespace google { +namespace protobuf { + +class Descriptor; +class EnumDescriptor; +class FieldDescriptor; +class OneofDescriptor; +class FileDescriptor; + +namespace io { class Printer; } + +namespace compiler { +namespace js { + +struct GeneratorOptions { + // Add a `goog.requires()` call for each enum type used. If not set, a forward + // declaration with `goog.forwardDeclare` is produced instead. + bool add_require_for_enums; + // Set this as a test-only module via `goog.setTestOnly();`. + bool testonly; + // Output path. + string output_dir; + // Namespace prefix. + string namespace_prefix; + // Create a library with name <name>_lib.js rather than a separate .js file + // per type? + string library; + // Error if there are two types that would generate the same output file? + bool error_on_name_conflict; + // Enable binary-format support? + bool binary; + + GeneratorOptions() + : add_require_for_enums(false), + testonly(false), + output_dir("."), + namespace_prefix(""), + library(""), + error_on_name_conflict(false), + binary(false) {} + + bool ParseFromOptions( + const vector< pair< string, string > >& options, + string* error); +}; + +class LIBPROTOC_EXPORT Generator : public CodeGenerator { + public: + Generator() {} + virtual ~Generator() {} + + virtual bool Generate(const FileDescriptor* file, + const string& parameter, + GeneratorContext* context, + string* error) const { + *error = "Unimplemented Generate() method. Call GenerateAll() instead."; + return false; + } + + virtual bool HasGenerateAll() const { return true; } + + virtual bool GenerateAll(const vector<const FileDescriptor*>& files, + const string& parameter, + GeneratorContext* context, + string* error) const; + + private: + void GenerateHeader(const GeneratorOptions& options, + io::Printer* printer) const; + + // Generate goog.provides() calls. + void FindProvides(const GeneratorOptions& options, + io::Printer* printer, + const vector<const FileDescriptor*>& file, + std::set<string>* provided) const; + void FindProvidesForMessage(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc, + std::set<string>* provided) const; + void FindProvidesForEnum(const GeneratorOptions& options, + io::Printer* printer, + const EnumDescriptor* enumdesc, + std::set<string>* provided) const; + // For extension fields at file scope. + void FindProvidesForFields(const GeneratorOptions& options, + io::Printer* printer, + const vector<const FieldDescriptor*>& fields, + std::set<string>* provided) const; + // Print the goog.provides() found by the methods above. + void GenerateProvides(const GeneratorOptions& options, + io::Printer* printer, + std::set<string>* provided) const; + + // Generate goog.setTestOnly() if indicated. + void GenerateTestOnly(const GeneratorOptions& options, + io::Printer* printer) const; + + // Generate goog.requires() calls. + void GenerateRequires(const GeneratorOptions& options, + io::Printer* printer, + const vector<const FileDescriptor*>& file, + std::set<string>* provided) const; + void GenerateRequires(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc, + std::set<string>* provided) const; + // For extension fields at file scope. + void GenerateRequires(const GeneratorOptions& options, + io::Printer* printer, + const vector<const FieldDescriptor*>& fields, + std::set<string>* provided) const; + void GenerateRequiresImpl(const GeneratorOptions& options, + io::Printer* printer, + std::set<string>* required, + std::set<string>* forwards, + std::set<string>* provided, + bool require_jspb, + bool require_extension) const; + void FindRequiresForMessage(const GeneratorOptions& options, + const Descriptor* desc, + std::set<string>* required, + std::set<string>* forwards, + bool* have_message) const; + void FindRequiresForField(const GeneratorOptions& options, + const FieldDescriptor* field, + std::set<string>* required, + std::set<string>* forwards) const; + void FindRequiresForExtension(const GeneratorOptions& options, + const FieldDescriptor* field, + std::set<string>* required, + std::set<string>* forwards) const; + + // Generate definitions for all message classes and enums in all files, + // processing the files in dependence order. + void GenerateFilesInDepOrder(const GeneratorOptions& options, + io::Printer* printer, + const vector<const FileDescriptor*>& file) const; + // Helper for above. + void GenerateFileAndDeps(const GeneratorOptions& options, + io::Printer* printer, + const FileDescriptor* root, + std::set<const FileDescriptor*>* all_files, + std::set<const FileDescriptor*>* generated) const; + + // Generate definitions for all message classes and enums. + void GenerateClassesAndEnums(const GeneratorOptions& options, + io::Printer* printer, + const FileDescriptor* file) const; + + // Generate definition for one class. + void GenerateClass(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const; + void GenerateClassConstructor(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const; + void GenerateClassFieldInfo(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const; + void GenerateClassXid(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const; + void GenerateOneofCaseDefinition(const GeneratorOptions& options, + io::Printer* printer, + const OneofDescriptor* oneof) const; + void GenerateClassToObject(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const; + void GenerateClassFieldToObject(const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* field) const; + void GenerateClassFromObject(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const; + void GenerateClassFieldFromObject(const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* field) const; + void GenerateClassClone(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const; + void GenerateClassRegistration(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const; + void GenerateClassFields(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const; + void GenerateClassField(const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* desc) const; + void GenerateClassExtensionFieldInfo(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const; + void GenerateClassDeserialize(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const; + void GenerateClassDeserializeBinary(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const; + void GenerateClassDeserializeBinaryField(const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* field) const; + void GenerateClassSerializeBinary(const GeneratorOptions& options, + io::Printer* printer, + const Descriptor* desc) const; + void GenerateClassSerializeBinaryField(const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* field) const; + + // Generate definition for one enum. + void GenerateEnum(const GeneratorOptions& options, + io::Printer* printer, + const EnumDescriptor* enumdesc) const; + + // Generate an extension definition. + void GenerateExtension(const GeneratorOptions& options, + io::Printer* printer, + const FieldDescriptor* field) const; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Generator); +}; + +} // namespace js +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JS_GENERATOR_H__ diff --git a/src/google/protobuf/compiler/main.cc b/src/google/protobuf/compiler/main.cc index 584e5a40..97df536e 100644 --- a/src/google/protobuf/compiler/main.cc +++ b/src/google/protobuf/compiler/main.cc @@ -38,6 +38,7 @@ #include <google/protobuf/compiler/ruby/ruby_generator.h> #include <google/protobuf/compiler/csharp/csharp_generator.h> #include <google/protobuf/compiler/objectivec/objectivec_generator.h> +#include <google/protobuf/compiler/js/js_generator.h> int main(int argc, char* argv[]) { @@ -80,5 +81,10 @@ int main(int argc, char* argv[]) { cli.RegisterGenerator("--objc_out", &objc_generator, "Generate Objective C header and source."); + // JavaScript + google::protobuf::compiler::js::Generator js_generator; + cli.RegisterGenerator("--js_out", &js_generator, + "Generate JavaScript source."); + return cli.Run(argc, argv); } diff --git a/src/google/protobuf/compiler/mock_code_generator.cc b/src/google/protobuf/compiler/mock_code_generator.cc index 98261431..121d917b 100644 --- a/src/google/protobuf/compiler/mock_code_generator.cc +++ b/src/google/protobuf/compiler/mock_code_generator.cc @@ -147,6 +147,12 @@ bool MockCodeGenerator::Generate( std::cerr << "Saw message type MockCodeGenerator_HasSourceCodeInfo: " << has_source_code_info << "." << std::endl; abort(); + } else if (command == "HasJsonName") { + FieldDescriptorProto field_descriptor_proto; + file->message_type(i)->field(0)->CopyTo(&field_descriptor_proto); + std::cerr << "Saw json_name: " + << field_descriptor_proto.has_json_name() << std::endl; + abort(); } else { GOOGLE_LOG(FATAL) << "Unknown MockCodeGenerator command: " << command; } diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc index a389a4fc..ea792a9d 100644 --- a/src/google/protobuf/compiler/parser.cc +++ b/src/google/protobuf/compiler/parser.cc @@ -458,6 +458,61 @@ void Parser::SkipRestOfBlock() { // =================================================================== +bool Parser::ValidateEnum(const EnumDescriptorProto* proto) { + bool has_allow_alias = false; + bool allow_alias = false; + + for (int i = 0; i < proto->options().uninterpreted_option_size(); i++) { + const UninterpretedOption option = proto->options().uninterpreted_option(i); + if (option.name_size() > 1) { + continue; + } + if (!option.name(0).is_extension() && + option.name(0).name_part() == "allow_alias") { + has_allow_alias = true; + if (option.identifier_value() == "true") { + allow_alias = true; + } + break; + } + } + + if (has_allow_alias && !allow_alias) { + string error = + "\"" + proto->name() + + "\" declares 'option allow_alias = false;' which has no effect. " + "Please remove the declaration."; + // This needlessly clutters declarations with nops. + AddError(error); + return false; + } + + set<int> used_values; + bool has_duplicates = false; + for (int i = 0; i < proto->value_size(); ++i) { + const EnumValueDescriptorProto enum_value = proto->value(i); + if (used_values.find(enum_value.number()) != used_values.end()) { + has_duplicates = true; + break; + } else { + used_values.insert(enum_value.number()); + } + } + if (allow_alias && !has_duplicates) { + string error = + "\"" + proto->name() + + "\" declares support for enum aliases but no enum values share field " + "numbers. Please remove the unnecessary 'option allow_alias = true;' " + "declaration."; + // Generate an error if an enum declares support for duplicate enum values + // and does not use it protect future authors. + AddError(error); + return false; + } + + return true; +} + bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) { input_ = input; had_errors_ = false; @@ -1627,6 +1682,9 @@ bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type, } DO(ParseEnumBlock(enum_type, enum_location, containing_file)); + + DO(ValidateEnum(enum_type)); + return true; } diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h index 3ba1e170..2c561c23 100644 --- a/src/google/protobuf/compiler/parser.h +++ b/src/google/protobuf/compiler/parser.h @@ -498,6 +498,8 @@ class LIBPROTOBUF_EXPORT Parser { } + bool ValidateEnum(const EnumDescriptorProto* proto); + // ================================================================= io::Tokenizer* input_; diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc index 0d729e0b..1d623dd9 100644 --- a/src/google/protobuf/compiler/parser_unittest.cc +++ b/src/google/protobuf/compiler/parser_unittest.cc @@ -1170,6 +1170,29 @@ TEST_F(ParseErrorTest, EnumValueOutOfRange) { "4:19: Integer out of range.\n"); } +TEST_F(ParseErrorTest, EnumAllowAliasFalse) { + ExpectHasErrors( + "enum Foo {\n" + " option allow_alias = false;\n" + " BAR = 1;\n" + " BAZ = 2;\n" + "}\n", + "5:0: \"Foo\" declares 'option allow_alias = false;' which has no effect. " + "Please remove the declaration.\n"); +} + +TEST_F(ParseErrorTest, UnnecessaryEnumAllowAlias) { + ExpectHasErrors( + "enum Foo {\n" + " option allow_alias = true;\n" + " BAR = 1;\n" + " BAZ = 2;\n" + "}\n", + "5:0: \"Foo\" declares support for enum aliases but no enum values share " + "field numbers. Please remove the unnecessary 'option allow_alias = true;' " + "declaration.\n"); +} + TEST_F(ParseErrorTest, DefaultValueMissing) { ExpectHasErrors( "message TestMessage {\n" @@ -1839,6 +1862,8 @@ TEST_F(ParseDescriptorDebugTest, TestCommentsInDebugString) { "// Detached comment before TestMessage1.\n" "\n" "// Message comment.\n" + "//\n" + "// More detail in message comment.\n" "message TestMessage1 {\n" "\n" " // Detached comment before foo.\n" @@ -1890,11 +1915,6 @@ TEST_F(ParseDescriptorDebugTest, TestCommentsInDebugString) { pool_.BuildFileCollectingErrors(parsed_desc, &collector); ASSERT_TRUE(descriptor != NULL); - DebugStringOptions debug_string_options; - debug_string_options.include_comments = true; - const string debug_string = - descriptor->DebugStringWithOptions(debug_string_options); - // Ensure that each of the comments appears somewhere in the DebugString(). // We don't test the exact comment placement or formatting, because we do not // want to be too fragile here. @@ -1905,6 +1925,7 @@ TEST_F(ParseDescriptorDebugTest, TestCommentsInDebugString) { "Package comment.", "Detached comment before TestMessage1.", "Message comment.", + "More detail in message comment.", "Detached comment before foo.", "Field comment", "Detached comment before NestedMessage.", @@ -1919,11 +1940,28 @@ TEST_F(ParseDescriptorDebugTest, TestCommentsInDebugString) { "RPC comment", }; - for (int i = 0; i < GOOGLE_ARRAYSIZE(expected_comments); ++i) { - string::size_type found_pos = debug_string.find(expected_comments[i]); - EXPECT_TRUE(found_pos != string::npos) - << "\"" << expected_comments[i] << "\" not found."; + DebugStringOptions debug_string_options; + debug_string_options.include_comments = true; + + { + const string debug_string = + descriptor->DebugStringWithOptions(debug_string_options); + + for (int i = 0; i < GOOGLE_ARRAYSIZE(expected_comments); ++i) { + string::size_type found_pos = debug_string.find(expected_comments[i]); + EXPECT_TRUE(found_pos != string::npos) + << "\"" << expected_comments[i] << "\" not found."; + } + + // Result of DebugStringWithOptions should be parseable. + SetupParser(debug_string.c_str()); + FileDescriptorProto parsed; + parser_->Parse(input_.get(), &parsed); + EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type); + ASSERT_EQ("", error_collector_.text_) + << "Failed to parse:\n" << debug_string; } + } TEST_F(ParseDescriptorDebugTest, TestMaps) { diff --git a/src/google/protobuf/compiler/plugin.pb.cc b/src/google/protobuf/compiler/plugin.pb.cc index 636673ae..a2da8eee 100644 --- a/src/google/protobuf/compiler/plugin.pb.cc +++ b/src/google/protobuf/compiler/plugin.pb.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/port.h> #include <google/protobuf/stubs/once.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/wire_format_lite_inl.h> diff --git a/src/google/protobuf/compiler/subprocess.cc b/src/google/protobuf/compiler/subprocess.cc index 85429924..a30ac305 100644 --- a/src/google/protobuf/compiler/subprocess.cc +++ b/src/google/protobuf/compiler/subprocess.cc @@ -47,6 +47,7 @@ #include <google/protobuf/message.h> #include <google/protobuf/stubs/substitute.h> + namespace google { namespace protobuf { namespace compiler { diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc index 9a1c6fe8..1aac360b 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -2050,6 +2050,7 @@ class SourceLocationCommentPrinter { } private: + bool have_source_loc_; SourceLocation source_loc_; DebugStringOptions options_; @@ -2951,7 +2952,8 @@ class DescriptorBuilder { const ServiceDescriptor* parent, MethodDescriptor* result); - void LogUnusedDependency(const FileDescriptor* result); + void LogUnusedDependency(const FileDescriptorProto& proto, + const FileDescriptor* result); // Must be run only after building. // @@ -3996,7 +3998,7 @@ const FileDescriptor* DescriptorBuilder::BuildFile( if (!unused_dependency_.empty()) { - LogUnusedDependency(result); + LogUnusedDependency(proto, result); } if (had_errors_) { @@ -4143,6 +4145,7 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, } } + void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, const Descriptor* parent, FieldDescriptor* result, @@ -4248,8 +4251,8 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, } else if (proto.default_value() == "nan") { result->default_value_float_ = numeric_limits<float>::quiet_NaN(); } else { - result->default_value_float_ = - io::NoLocaleStrtod(proto.default_value().c_str(), &end_pos); + result->default_value_float_ = io::SafeDoubleToFloat( + io::NoLocaleStrtod(proto.default_value().c_str(), &end_pos)); } break; case FieldDescriptor::CPPTYPE_DOUBLE: @@ -4421,6 +4424,7 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, AllocateOptions(proto.options(), result); } + AddSymbol(result->full_name(), parent, result->name(), proto, Symbol(result)); } @@ -5533,11 +5537,19 @@ bool DescriptorBuilder::OptionInterpreter::InterpretOptions( // UnknownFieldSet and wait there until the message is parsed by something // that does know about the options. string buf; - options->AppendToString(&buf); - GOOGLE_CHECK(options->ParseFromString(buf)) + GOOGLE_CHECK(options->AppendPartialToString(&buf)) + << "Protocol message could not be serialized."; + GOOGLE_CHECK(options->ParsePartialFromString(buf)) << "Protocol message serialized itself in invalid fashion."; + if (!options->IsInitialized()) { + builder_->AddWarning( + options_to_interpret->element_name, *original_options, + DescriptorPool::ErrorCollector::OTHER, + "Options could not be fully parsed using the proto descriptors " + "compiled into this binary. Missing required fields: " + + options->InitializationErrorString()); + } } - return !failed; } @@ -6191,7 +6203,8 @@ void DescriptorBuilder::OptionInterpreter::SetUInt64(int number, uint64 value, } } -void DescriptorBuilder::LogUnusedDependency(const FileDescriptor* result) { +void DescriptorBuilder::LogUnusedDependency(const FileDescriptorProto& proto, + const FileDescriptor* result) { if (!unused_dependency_.empty()) { std::set<string> annotation_extensions; @@ -6217,9 +6230,9 @@ void DescriptorBuilder::LogUnusedDependency(const FileDescriptor* result) { } // Log warnings for unused imported files. if (i == (*it)->extension_count()) { - GOOGLE_LOG(WARNING) << "Warning: Unused import: \"" << result->name() - << "\" imports \"" << (*it)->name() - << "\" which is not used."; + string error_message = "Import " + (*it)->name() + " but not used."; + AddWarning((*it)->name(), proto, DescriptorPool::ErrorCollector::OTHER, + error_message); } } } diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc index bc846609..eda6280f 100644 --- a/src/google/protobuf/descriptor.pb.cc +++ b/src/google/protobuf/descriptor.pb.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/port.h> #include <google/protobuf/stubs/once.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/wire_format_lite_inl.h> diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc index ccd0650d..be8e0b72 100644 --- a/src/google/protobuf/descriptor_unittest.cc +++ b/src/google/protobuf/descriptor_unittest.cc @@ -178,6 +178,61 @@ void AddEmptyEnum(FileDescriptorProto* file, const string& name) { AddEnumValue(AddEnum(file, name), name + "_DUMMY", 1); } +class MockErrorCollector : public DescriptorPool::ErrorCollector { + public: + MockErrorCollector() {} + ~MockErrorCollector() {} + + string text_; + string warning_text_; + + // implements ErrorCollector --------------------------------------- + void AddError(const string& filename, + const string& element_name, const Message* descriptor, + ErrorLocation location, const string& message) { + const char* location_name = NULL; + switch (location) { + case NAME : location_name = "NAME" ; break; + case NUMBER : location_name = "NUMBER" ; break; + case TYPE : location_name = "TYPE" ; break; + case EXTENDEE : location_name = "EXTENDEE" ; break; + case DEFAULT_VALUE: location_name = "DEFAULT_VALUE"; break; + case OPTION_NAME : location_name = "OPTION_NAME" ; break; + case OPTION_VALUE : location_name = "OPTION_VALUE" ; break; + case INPUT_TYPE : location_name = "INPUT_TYPE" ; break; + case OUTPUT_TYPE : location_name = "OUTPUT_TYPE" ; break; + case OTHER : location_name = "OTHER" ; break; + } + + strings::SubstituteAndAppend( + &text_, "$0: $1: $2: $3\n", + filename, element_name, location_name, message); + } + + // implements ErrorCollector --------------------------------------- + void AddWarning(const string& filename, const string& element_name, + const Message* descriptor, ErrorLocation location, + const string& message) { + const char* location_name = NULL; + switch (location) { + case NAME : location_name = "NAME" ; break; + case NUMBER : location_name = "NUMBER" ; break; + case TYPE : location_name = "TYPE" ; break; + case EXTENDEE : location_name = "EXTENDEE" ; break; + case DEFAULT_VALUE: location_name = "DEFAULT_VALUE"; break; + case OPTION_NAME : location_name = "OPTION_NAME" ; break; + case OPTION_VALUE : location_name = "OPTION_VALUE" ; break; + case INPUT_TYPE : location_name = "INPUT_TYPE" ; break; + case OUTPUT_TYPE : location_name = "OUTPUT_TYPE" ; break; + case OTHER : location_name = "OTHER" ; break; + } + + strings::SubstituteAndAppend( + &warning_text_, "$0: $1: $2: $3\n", + filename, element_name, location_name, message); + } +}; + // =================================================================== // Test simple files. @@ -3120,77 +3175,95 @@ TEST(CustomOptions, UnusedImportWarning) { ->file()->CopyTo(&file_proto); ASSERT_TRUE(pool.BuildFile(file_proto) != NULL); - pool.AddUnusedImportTrackFile("custom_options_import.proto"); ASSERT_TRUE(TextFormat::ParseFromString( "name: \"custom_options_import.proto\" " "package: \"protobuf_unittest\" " "dependency: \"google/protobuf/unittest_custom_options.proto\" ", &file_proto)); - pool.BuildFile(file_proto); -} -// =================================================================== + MockErrorCollector error_collector; + EXPECT_TRUE(pool.BuildFileCollectingErrors(file_proto, &error_collector)); + EXPECT_EQ("", error_collector.warning_text_); +} -// The tests below trigger every unique call to AddError() in descriptor.cc, -// in the order in which they appear in that file. I'm using TextFormat here -// to specify the input descriptors because building them using code would -// be too bulky. +// Verifies that proto files can correctly be parsed, even if the +// custom options defined in the file are incompatible with those +// compiled in the binary. See http://b/19276250. +TEST(CustomOptions, OptionsWithRequiredEnums) { + DescriptorPool pool; -class MockErrorCollector : public DescriptorPool::ErrorCollector { - public: - MockErrorCollector() {} - ~MockErrorCollector() {} + FileDescriptorProto file_proto; + MessageOptions::descriptor()->file()->CopyTo(&file_proto); + ASSERT_TRUE(pool.BuildFile(file_proto) != NULL); - string text_; - string warning_text_; + // Create a new file descriptor proto containing a subset of the + // messages defined in google/protobuf/unittest_custom_options.proto. + file_proto.Clear(); + file_proto.set_name("unittest_custom_options.proto"); + file_proto.set_package("protobuf_unittest"); + file_proto.add_dependency("google/protobuf/descriptor.proto"); - // implements ErrorCollector --------------------------------------- - void AddError(const string& filename, - const string& element_name, const Message* descriptor, - ErrorLocation location, const string& message) { - const char* location_name = NULL; - switch (location) { - case NAME : location_name = "NAME" ; break; - case NUMBER : location_name = "NUMBER" ; break; - case TYPE : location_name = "TYPE" ; break; - case EXTENDEE : location_name = "EXTENDEE" ; break; - case DEFAULT_VALUE: location_name = "DEFAULT_VALUE"; break; - case OPTION_NAME : location_name = "OPTION_NAME" ; break; - case OPTION_VALUE : location_name = "OPTION_VALUE" ; break; - case INPUT_TYPE : location_name = "INPUT_TYPE" ; break; - case OUTPUT_TYPE : location_name = "OUTPUT_TYPE" ; break; - case OTHER : location_name = "OTHER" ; break; - } + // Add the "required_enum_opt" extension. + FieldDescriptorProto* extension = file_proto.add_extension(); + protobuf_unittest::OldOptionType::descriptor()->file() + ->FindExtensionByName("required_enum_opt")->CopyTo(extension); + + // Add a test message that uses the "required_enum_opt" option. + DescriptorProto* test_message_type = file_proto.add_message_type(); + protobuf_unittest::TestMessageWithRequiredEnumOption::descriptor() + ->CopyTo(test_message_type); + + // Instruct the extension to use NewOptionType instead of + // OldOptionType, and add the descriptor of NewOptionType. + extension->set_type_name(".protobuf_unittest.NewOptionType"); + DescriptorProto* new_option_type = file_proto.add_message_type(); + protobuf_unittest::NewOptionType::descriptor() + ->CopyTo(new_option_type); + + // Replace the value of the "required_enum_opt" option used in the + // test message with an enum value that only exists in NewOptionType. + ASSERT_TRUE(TextFormat::ParseFromString( + "uninterpreted_option { " + " name { " + " name_part: 'required_enum_opt' " + " is_extension: true " + " } " + " aggregate_value: 'value: NEW_VALUE' " + "}", + test_message_type->mutable_options())); - strings::SubstituteAndAppend( - &text_, "$0: $1: $2: $3\n", - filename, element_name, location_name, message); - } + // Add the file descriptor to the pool. + ASSERT_TRUE(pool.BuildFile(file_proto) != NULL); - // implements ErrorCollector --------------------------------------- - void AddWarning(const string& filename, const string& element_name, - const Message* descriptor, ErrorLocation location, - const string& message) { - const char* location_name = NULL; - switch (location) { - case NAME : location_name = "NAME" ; break; - case NUMBER : location_name = "NUMBER" ; break; - case TYPE : location_name = "TYPE" ; break; - case EXTENDEE : location_name = "EXTENDEE" ; break; - case DEFAULT_VALUE: location_name = "DEFAULT_VALUE"; break; - case OPTION_NAME : location_name = "OPTION_NAME" ; break; - case OPTION_VALUE : location_name = "OPTION_VALUE" ; break; - case INPUT_TYPE : location_name = "INPUT_TYPE" ; break; - case OUTPUT_TYPE : location_name = "OUTPUT_TYPE" ; break; - case OTHER : location_name = "OTHER" ; break; - } + // Find the test message. + const Descriptor* test_message = pool.FindMessageTypeByName( + "protobuf_unittest.TestMessageWithRequiredEnumOption"); + ASSERT_TRUE(test_message != NULL); + + const MessageOptions& options = test_message->options(); + // Extract the "required_enum_opt" option. Since the binary does not + // know that the extension was updated, this will still return an + // OldOptionType message. + ASSERT_TRUE( + options.HasExtension(protobuf_unittest::required_enum_opt)); + const protobuf_unittest::OldOptionType& old_enum_opt = + options.GetExtension(protobuf_unittest::required_enum_opt); + + // Confirm that the required enum field is missing. + EXPECT_FALSE(old_enum_opt.IsInitialized()); + EXPECT_FALSE(old_enum_opt.has_value()); + + string buf; + // Verify that the required enum field does show up when the option + // is re-parsed as a NewOptionType message; + protobuf_unittest::NewOptionType new_enum_opt; + EXPECT_TRUE(old_enum_opt.AppendPartialToString(&buf)); + EXPECT_TRUE(new_enum_opt.ParseFromString(buf)); + EXPECT_EQ(protobuf_unittest::NewOptionType::NEW_VALUE, new_enum_opt.value()); +} - strings::SubstituteAndAppend( - &warning_text_, "$0: $1: $2: $3\n", - filename, element_name, location_name, message); - } -}; +// =================================================================== class ValidationErrorTest : public testing::Test { protected: @@ -5123,7 +5196,6 @@ TEST_F(ValidationErrorTest, AllowEnumAlias) { } TEST_F(ValidationErrorTest, UnusedImportWarning) { - pool_.AddUnusedImportTrackFile("bar.proto"); BuildFile( "name: \"bar.proto\" " @@ -5155,7 +5227,7 @@ TEST_F(ValidationErrorTest, UnusedImportWarning) { // } // pool_.AddUnusedImportTrackFile("forward.proto"); - BuildFile( + BuildFileWithWarnings( "name: \"forward.proto\"" "dependency: \"base.proto\"" "dependency: \"bar.proto\"" @@ -5165,7 +5237,8 @@ TEST_F(ValidationErrorTest, UnusedImportWarning) { "message_type {" " name: \"Forward\"" " field { name:\"base\" number:1 label:LABEL_OPTIONAL type_name:\"Base\" }" - "}"); + "}", + "forward.proto: bar.proto: OTHER: Import bar.proto but not used.\n"); } namespace { diff --git a/src/google/protobuf/duration.pb.cc b/src/google/protobuf/duration.pb.cc index c7eed0d5..b325944e 100644 --- a/src/google/protobuf/duration.pb.cc +++ b/src/google/protobuf/duration.pb.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/port.h> #include <google/protobuf/stubs/once.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/wire_format_lite_inl.h> diff --git a/src/google/protobuf/duration.proto b/src/google/protobuf/duration.proto index 7f172aa6..78bcc74b 100644 --- a/src/google/protobuf/duration.proto +++ b/src/google/protobuf/duration.proto @@ -27,15 +27,16 @@ // 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 google.protobuf; -option java_generate_equals_and_hash = true; -option java_multiple_files = true; -option java_outer_classname = "DurationProto"; -option java_package = "com.google.protobuf"; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DurationProto"; +option java_multiple_files = true; +option java_generate_equals_and_hash = true; option objc_class_prefix = "GPB"; // A Duration represents a signed, fixed-length span of time represented diff --git a/src/google/protobuf/dynamic_message.cc b/src/google/protobuf/dynamic_message.cc index 091fc975..bb400476 100644 --- a/src/google/protobuf/dynamic_message.cc +++ b/src/google/protobuf/dynamic_message.cc @@ -69,8 +69,8 @@ #include <google/protobuf/stubs/shared_ptr.h> #endif -#include <google/protobuf/stubs/scoped_ptr.h> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/scoped_ptr.h> #include <google/protobuf/dynamic_message.h> #include <google/protobuf/descriptor.h> diff --git a/src/google/protobuf/empty.pb.cc b/src/google/protobuf/empty.pb.cc index 4a4f6eae..f2eec782 100644 --- a/src/google/protobuf/empty.pb.cc +++ b/src/google/protobuf/empty.pb.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/port.h> #include <google/protobuf/stubs/once.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/wire_format_lite_inl.h> @@ -79,9 +80,9 @@ void protobuf_AddDesc_google_2fprotobuf_2fempty_2eproto() { ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( "\n\033google/protobuf/empty.proto\022\017google.pr" - "otobuf\"\007\n\005EmptyBM\n\023com.google.protobufB\n" - "EmptyProtoP\001\240\001\001\242\002\003GPB\252\002\036Google.Protobuf." - "WellKnownTypesb\006proto3", 142); + "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); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/empty.proto", &protobuf_RegisterTypes); Empty::default_instance_ = new Empty(); @@ -117,6 +118,14 @@ Empty::Empty() // @@protoc_insertion_point(constructor:google.protobuf.Empty) } +Empty::Empty(::google::protobuf::Arena* arena) + : ::google::protobuf::Message(), + _internal_metadata_(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:google.protobuf.Empty) +} + void Empty::InitAsDefaultInstance() { _is_default_instance_ = true; } @@ -140,10 +149,20 @@ Empty::~Empty() { } void Empty::SharedDtor() { + if (GetArenaNoVirtual() != NULL) { + return; + } + if (this != default_instance_) { } } +void Empty::ArenaDtor(void* object) { + Empty* _this = reinterpret_cast< Empty* >(object); + (void)_this; +} +void Empty::RegisterArenaDtor(::google::protobuf::Arena* arena) { +} void Empty::SetCachedSize(int size) const { GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = size; @@ -162,11 +181,7 @@ const Empty& Empty::default_instance() { Empty* Empty::default_instance_ = NULL; Empty* Empty::New(::google::protobuf::Arena* arena) const { - Empty* n = new Empty; - if (arena != NULL) { - arena->Own(n); - } - return n; + return ::google::protobuf::Arena::CreateMessage<Empty>(arena); } void Empty::Clear() { @@ -255,6 +270,18 @@ bool Empty::IsInitialized() const { void Empty::Swap(Empty* other) { if (other == this) return; + if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) { + InternalSwap(other); + } else { + Empty temp; + temp.MergeFrom(*this); + CopyFrom(*other); + other->CopyFrom(temp); + } +} +void Empty::UnsafeArenaSwap(Empty* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual()); InternalSwap(other); } void Empty::InternalSwap(Empty* other) { diff --git a/src/google/protobuf/empty.pb.h b/src/google/protobuf/empty.pb.h index 20876bea..868009fc 100644 --- a/src/google/protobuf/empty.pb.h +++ b/src/google/protobuf/empty.pb.h @@ -53,9 +53,14 @@ class LIBPROTOBUF_EXPORT Empty : public ::google::protobuf::Message { return *this; } + inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); } + inline void* GetMaybeArenaPointer() const { + return MaybeArenaPtr(); + } static const ::google::protobuf::Descriptor* descriptor(); static const Empty& default_instance(); + void UnsafeArenaSwap(Empty* other); void Swap(Empty* other); // implements Message ---------------------------------------------- @@ -82,6 +87,11 @@ class LIBPROTOBUF_EXPORT Empty : public ::google::protobuf::Message { void SharedDtor(); void SetCachedSize(int size) const; void InternalSwap(Empty* other); + protected: + explicit Empty(::google::protobuf::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::google::protobuf::Arena* arena); private: inline ::google::protobuf::Arena* GetArenaNoVirtual() const { return _internal_metadata_.arena(); @@ -101,6 +111,9 @@ class LIBPROTOBUF_EXPORT Empty : public ::google::protobuf::Message { private: ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; bool _is_default_instance_; mutable int _cached_size_; friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fempty_2eproto(); diff --git a/src/google/protobuf/empty.proto b/src/google/protobuf/empty.proto index 9dddc6c5..b96daf28 100644 --- a/src/google/protobuf/empty.proto +++ b/src/google/protobuf/empty.proto @@ -27,16 +27,18 @@ // 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 google.protobuf; -option java_multiple_files = true; -option java_outer_classname = "EmptyProto"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; option java_generate_equals_and_hash = true; -option csharp_namespace = "Google.Protobuf.WellKnownTypes"; option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; // A generic empty message that you can re-use to avoid defining duplicated // empty messages in your APIs. A typical example is to use it as the request diff --git a/src/google/protobuf/field_mask.pb.cc b/src/google/protobuf/field_mask.pb.cc index f834363b..01a6ce56 100644 --- a/src/google/protobuf/field_mask.pb.cc +++ b/src/google/protobuf/field_mask.pb.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/port.h> #include <google/protobuf/stubs/once.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/wire_format_lite_inl.h> diff --git a/src/google/protobuf/field_mask.proto b/src/google/protobuf/field_mask.proto index 8b21c692..908c8a86 100644 --- a/src/google/protobuf/field_mask.proto +++ b/src/google/protobuf/field_mask.proto @@ -27,16 +27,17 @@ // 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 google.protobuf; -option java_generate_equals_and_hash = true; -option java_multiple_files = true; -option java_outer_classname = "FieldMaskProto"; -option java_package = "com.google.protobuf"; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "FieldMaskProto"; +option java_multiple_files = true; option objc_class_prefix = "GPB"; +option java_generate_equals_and_hash = true; // `FieldMask` represents a set of symbolic field paths, for example: // @@ -69,7 +70,7 @@ option objc_class_prefix = "GPB"; // z: 8 // // The result will not contain specific values for fields x,y and z -// (there value will be set to the default, and omitted in proto text +// (their value will be set to the default, and omitted in proto text // output): // // diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h index cb1869a7..2da096c5 100644 --- a/src/google/protobuf/io/coded_stream.h +++ b/src/google/protobuf/io/coded_stream.h @@ -109,6 +109,7 @@ #ifndef GOOGLE_PROTOBUF_IO_CODED_STREAM_H__ #define GOOGLE_PROTOBUF_IO_CODED_STREAM_H__ +#include <assert.h> #include <string> #include <utility> #ifdef _MSC_VER diff --git a/src/google/protobuf/io/strtod.cc b/src/google/protobuf/io/strtod.cc index 579de9aa..a90bb9a3 100644 --- a/src/google/protobuf/io/strtod.cc +++ b/src/google/protobuf/io/strtod.cc @@ -32,6 +32,7 @@ #include <cstdio> #include <cstring> +#include <limits> #include <string> #include <google/protobuf/stubs/logging.h> @@ -109,6 +110,16 @@ double NoLocaleStrtod(const char* text, char** original_endptr) { return result; } +float SafeDoubleToFloat(double value) { + if (value > std::numeric_limits<float>::max()) { + return std::numeric_limits<float>::infinity(); + } else if (value < -std::numeric_limits<float>::max()) { + return -std::numeric_limits<float>::infinity(); + } else { + return static_cast<float>(value); + } +} + } // namespace io } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/io/strtod.h b/src/google/protobuf/io/strtod.h index c2efc8d3..f56e41c8 100644 --- a/src/google/protobuf/io/strtod.h +++ b/src/google/protobuf/io/strtod.h @@ -43,6 +43,11 @@ namespace io { // uses a dot as the decimal separator. double NoLocaleStrtod(const char* str, char** endptr); +// Casts a double value to a float value. If the value is outside of the +// representable range of float, it will be converted to positive or negative +// infinity. +float SafeDoubleToFloat(double value); + } // namespace io } // namespace protobuf diff --git a/src/google/protobuf/io/tokenizer.cc b/src/google/protobuf/io/tokenizer.cc index 7ccd633d..3d57707c 100644 --- a/src/google/protobuf/io/tokenizer.cc +++ b/src/google/protobuf/io/tokenizer.cc @@ -375,7 +375,7 @@ void Tokenizer::ConsumeString(char delimiter) { // Possibly followed by two more octal digits, but these will // just be consumed by the main loop anyway so we don't need // to do so explicitly here. - } else if (TryConsume('x') || TryConsume('X')) { + } else if (TryConsume('x')) { if (!TryConsumeOne<HexDigit>()) { AddError("Expected hex digits for escape sequence."); } diff --git a/src/google/protobuf/io/tokenizer_unittest.cc b/src/google/protobuf/io/tokenizer_unittest.cc index 6526056a..20d50a2c 100644 --- a/src/google/protobuf/io/tokenizer_unittest.cc +++ b/src/google/protobuf/io/tokenizer_unittest.cc @@ -875,6 +875,8 @@ ErrorCase kErrorCases[] = { // String errors. { "'\\l' foo", true, "0:2: Invalid escape sequence in string literal.\n" }, + { "'\\X' foo", true, + "0:2: Invalid escape sequence in string literal.\n" }, { "'\\x' foo", true, "0:3: Expected hex digits for escape sequence.\n" }, { "'foo", false, diff --git a/src/google/protobuf/map.h b/src/google/protobuf/map.h index 4bd76f25..dfc62420 100644 --- a/src/google/protobuf/map.h +++ b/src/google/protobuf/map.h @@ -188,7 +188,6 @@ class LIBPROTOBUF_EXPORT MapKey { case FieldDescriptor::CPPTYPE_ENUM: case FieldDescriptor::CPPTYPE_MESSAGE: GOOGLE_LOG(FATAL) << "Can't get here."; - return false; } GOOGLE_LOG(FATAL) << "Can't get here."; return false; @@ -497,7 +496,7 @@ class Map { insert(other.begin(), other.end()); } template <class InputIt> - explicit Map(const InputIt& first, const InputIt& last) + Map(const InputIt& first, const InputIt& last) : arena_(NULL), allocator_(arena_), elements_(0, hasher(), key_equal(), allocator_), @@ -546,9 +545,7 @@ class Map { } #if __cplusplus >= 201103L && !defined(GOOGLE_PROTOBUF_OS_APPLE) && \ - !defined(GOOGLE_PROTOBUF_OS_NACL) && \ - !defined(GOOGLE_PROTOBUF_OS_ANDROID) && \ - !defined(GOOGLE_PROTOBUF_OS_EMSCRIPTEN) + !defined(GOOGLE_PROTOBUF_OS_NACL) && !defined(GOOGLE_PROTOBUF_OS_ANDROID) template<class NodeType, class... Args> void construct(NodeType* p, Args&&... args) { new (static_cast<void*>(p)) NodeType(std::forward<Args>(args)...); @@ -586,21 +583,22 @@ class Map { private: typedef void DestructorSkippable_; - Arena* arena_; + Arena* const arena_; template <typename X> friend class MapAllocator; }; - public: typedef MapAllocator<std::pair<const Key, MapPair<Key, T>*> > Allocator; + typedef hash_map<Key, value_type*, hash<Key>, equal_to<Key>, Allocator> + InnerMap; + public: // Iterators class const_iterator : public std::iterator<std::forward_iterator_tag, value_type, ptrdiff_t, const value_type*, const value_type&> { - typedef typename hash_map<Key, value_type*, hash<Key>, equal_to<Key>, - Allocator>::const_iterator InnerIt; + typedef typename InnerMap::const_iterator InnerIt; public: const_iterator() {} @@ -627,8 +625,7 @@ class Map { }; class iterator : public std::iterator<std::forward_iterator_tag, value_type> { - typedef typename hash_map<Key, value_type*, hasher, equal_to<Key>, - Allocator>::iterator InnerIt; + typedef typename InnerMap::iterator InnerIt; public: iterator() {} @@ -744,8 +741,7 @@ class Map { // Erase size_type erase(const key_type& key) { - typename hash_map<Key, value_type*, hash<Key>, equal_to<Key>, - Allocator>::iterator it = elements_.find(key); + typename InnerMap::iterator it = elements_.find(key); if (it == elements_.end()) { return 0; } else { @@ -815,7 +811,7 @@ class Map { Arena* arena_; Allocator allocator_; - hash_map<Key, value_type*, hash<Key>, equal_to<Key>, Allocator> elements_; + InnerMap elements_; int default_enum_value_; friend class ::google::protobuf::Arena; @@ -854,7 +850,6 @@ struct hash<google::protobuf::MapKey> { case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: GOOGLE_LOG(FATAL) << "Can't get here."; - return 0; } GOOGLE_LOG(FATAL) << "Can't get here."; return 0; @@ -879,7 +874,6 @@ struct hash<google::protobuf::MapKey> { case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: GOOGLE_LOG(FATAL) << "Can't get here."; - return true; } GOOGLE_LOG(FATAL) << "Can't get here."; return true; diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h index 5530fefe..432236ce 100644 --- a/src/google/protobuf/repeated_field.h +++ b/src/google/protobuf/repeated_field.h @@ -208,10 +208,19 @@ class RepeatedField { // sizeof(*this) int SpaceUsedExcludingSelf() const; - // Remove the element referenced by position. + // Removes the element referenced by position. + // + // Returns an iterator to the element immediately following the removed + // element. + // + // Invalidates all iterators at or after the removed element, including end(). iterator erase(const_iterator position); - // Remove the elements in the range [first, last). + // Removes the elements in the range [first, last). + // + // Returns an iterator to the element immediately following the removed range. + // + // Invalidates all iterators at or after the removed range, including end(). iterator erase(const_iterator first, const_iterator last); // Get the Arena on which this RepeatedField stores its elements. @@ -885,10 +894,19 @@ class RepeatedPtrField : public internal::RepeatedPtrFieldBase { // so will trigger a GOOGLE_DCHECK-failure. Element* ReleaseCleared(); - // Remove the element referenced by position. + // Removes the element referenced by position. + // + // Returns an iterator to the element immediately following the removed + // element. + // + // Invalidates all iterators at or after the removed element, including end(). iterator erase(const_iterator position); // Removes the elements in the range [first, last). + // + // Returns an iterator to the element immediately following the removed range. + // + // Invalidates all iterators at or after the removed range, including end(). iterator erase(const_iterator first, const_iterator last); // Gets the arena on which this RepeatedPtrField stores its elements. diff --git a/src/google/protobuf/source_context.pb.cc b/src/google/protobuf/source_context.pb.cc index 1be3297e..f2eb7ae7 100644 --- a/src/google/protobuf/source_context.pb.cc +++ b/src/google/protobuf/source_context.pb.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/port.h> #include <google/protobuf/stubs/once.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/wire_format_lite_inl.h> diff --git a/src/google/protobuf/source_context.proto b/src/google/protobuf/source_context.proto index e9a27d65..d76252ca 100644 --- a/src/google/protobuf/source_context.proto +++ b/src/google/protobuf/source_context.proto @@ -27,15 +27,16 @@ // 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 google.protobuf; -option java_multiple_files = true; -option java_outer_classname = "SourceContextProto"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; option java_package = "com.google.protobuf"; +option java_outer_classname = "SourceContextProto"; +option java_multiple_files = true; option java_generate_equals_and_hash = true; -option csharp_namespace = "Google.Protobuf.WellKnownTypes"; option objc_class_prefix = "GPB"; // `SourceContext` represents information about the source of a diff --git a/src/google/protobuf/struct.pb.cc b/src/google/protobuf/struct.pb.cc index 273645d9..e020597a 100644 --- a/src/google/protobuf/struct.pb.cc +++ b/src/google/protobuf/struct.pb.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/port.h> #include <google/protobuf/stubs/once.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/wire_format_lite_inl.h> diff --git a/src/google/protobuf/struct.proto b/src/google/protobuf/struct.proto index b3e9e699..8562e2c1 100644 --- a/src/google/protobuf/struct.proto +++ b/src/google/protobuf/struct.proto @@ -27,15 +27,16 @@ // 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 google.protobuf; -option java_generate_equals_and_hash = true; -option java_multiple_files = true; -option java_outer_classname = "StructProto"; -option java_package = "com.google.protobuf"; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "StructProto"; +option java_multiple_files = true; +option java_generate_equals_and_hash = true; option objc_class_prefix = "GPB"; diff --git a/src/google/protobuf/stubs/strutil.cc b/src/google/protobuf/stubs/strutil.cc index 8442f2ce..7ba92e8f 100644 --- a/src/google/protobuf/stubs/strutil.cc +++ b/src/google/protobuf/stubs/strutil.cc @@ -524,27 +524,81 @@ int CEscapeInternal(const char* src, int src_len, char* dest, return used; } -int CEscapeString(const char* src, int src_len, char* dest, int dest_len) { - return CEscapeInternal(src, src_len, dest, dest_len, false, false); +// Calculates the length of the C-style escaped version of 'src'. +// Assumes that non-printable characters are escaped using octal sequences, and +// that UTF-8 bytes are not handled specially. +static inline size_t CEscapedLength(StringPiece src) { + static char c_escaped_len[256] = { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 2, 4, 4, // \t, \n, \r + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // ", ' + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // '0'..'9' + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'A'..'O' + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, // 'P'..'Z', '\' + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'a'..'o' + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, // 'p'..'z', DEL + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + }; + + size_t escaped_len = 0; + for (int i = 0; i < src.size(); ++i) { + unsigned char c = static_cast<unsigned char>(src[i]); + escaped_len += c_escaped_len[c]; + } + return escaped_len; } // ---------------------------------------------------------------------- -// CEscape() -// CHexEscape() -// Copies 'src' to result, escaping dangerous characters using -// C-style escape sequences. This is very useful for preparing query -// flags. 'src' and 'dest' should not overlap. The 'Hex' version -// hexadecimal rather than octal sequences. -// -// Currently only \n, \r, \t, ", ', \ and !isprint() chars are escaped. +// Escapes 'src' using C-style escape sequences, and appends the escaped string +// to 'dest'. This version is faster than calling CEscapeInternal as it computes +// the required space using a lookup table, and also does not do any special +// handling for Hex or UTF-8 characters. // ---------------------------------------------------------------------- +void CEscapeAndAppend(StringPiece src, string* dest) { + size_t escaped_len = CEscapedLength(src); + if (escaped_len == src.size()) { + dest->append(src.data(), src.size()); + return; + } + + size_t cur_dest_len = dest->size(); + dest->resize(cur_dest_len + escaped_len); + char* append_ptr = &(*dest)[cur_dest_len]; + + for (int i = 0; i < src.size(); ++i) { + unsigned char c = static_cast<unsigned char>(src[i]); + switch (c) { + case '\n': *append_ptr++ = '\\'; *append_ptr++ = 'n'; break; + case '\r': *append_ptr++ = '\\'; *append_ptr++ = 'r'; break; + case '\t': *append_ptr++ = '\\'; *append_ptr++ = 't'; break; + case '\"': *append_ptr++ = '\\'; *append_ptr++ = '\"'; break; + case '\'': *append_ptr++ = '\\'; *append_ptr++ = '\''; break; + case '\\': *append_ptr++ = '\\'; *append_ptr++ = '\\'; break; + default: + if (!isprint(c)) { + *append_ptr++ = '\\'; + *append_ptr++ = '0' + c / 64; + *append_ptr++ = '0' + (c % 64) / 8; + *append_ptr++ = '0' + c % 8; + } else { + *append_ptr++ = c; + } + break; + } + } +} + string CEscape(const string& src) { - const int dest_length = src.size() * 4 + 1; // Maximum possible expansion - scoped_array<char> dest(new char[dest_length]); - const int len = CEscapeInternal(src.data(), src.size(), - dest.get(), dest_length, false, false); - GOOGLE_DCHECK_GE(len, 0); - return string(dest.get(), len); + string dest; + CEscapeAndAppend(src, &dest); + return dest; } namespace strings { diff --git a/src/google/protobuf/stubs/strutil.h b/src/google/protobuf/stubs/strutil.h index b22066b6..27d47575 100644 --- a/src/google/protobuf/stubs/strutil.h +++ b/src/google/protobuf/stubs/strutil.h @@ -314,26 +314,20 @@ LIBPROTOBUF_EXPORT int UnescapeCEscapeString(const string& src, string* dest, LIBPROTOBUF_EXPORT string UnescapeCEscapeString(const string& src); // ---------------------------------------------------------------------- -// CEscapeString() -// Copies 'src' to 'dest', escaping dangerous characters using -// C-style escape sequences. This is very useful for preparing query -// flags. 'src' and 'dest' should not overlap. -// Returns the number of bytes written to 'dest' (not including the \0) -// or -1 if there was insufficient space. +// CEscape() +// Escapes 'src' using C-style escape sequences and returns the resulting +// string. // -// Currently only \n, \r, \t, ", ', \ and !isprint() chars are escaped. +// Escaped chars: \n, \r, \t, ", ', \, and !isprint(). // ---------------------------------------------------------------------- -LIBPROTOBUF_EXPORT int CEscapeString(const char* src, int src_len, - char* dest, int dest_len); +LIBPROTOBUF_EXPORT string CEscape(const string& src); // ---------------------------------------------------------------------- -// CEscape() -// More convenient form of CEscapeString: returns result as a "string". -// This version is slower than CEscapeString() because it does more -// allocation. However, it is much more convenient to use in -// non-speed-critical code like logging messages etc. +// CEscapeAndAppend() +// Escapes 'src' using C-style escape sequences, and appends the escaped +// string to 'dest'. // ---------------------------------------------------------------------- -LIBPROTOBUF_EXPORT string CEscape(const string& src); +LIBPROTOBUF_EXPORT void CEscapeAndAppend(StringPiece src, string* dest); namespace strings { // Like CEscape() but does not escape bytes with the upper bit set. diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc index 38b9069b..b0a5ce63 100644 --- a/src/google/protobuf/text_format.cc +++ b/src/google/protobuf/text_format.cc @@ -46,6 +46,7 @@ #include <google/protobuf/dynamic_message.h> #include <google/protobuf/repeated_field.h> #include <google/protobuf/wire_format_lite.h> +#include <google/protobuf/io/strtod.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/io/zero_copy_stream.h> #include <google/protobuf/io/zero_copy_stream_impl.h> @@ -380,6 +381,7 @@ class TextFormat::Parser::ParserImpl { string full_type_name, prefix; DO(ConsumeAnyTypeUrl(&full_type_name, &prefix)); DO(Consume("]")); + TryConsume(":"); // ':' is optional between message labels and values. string serialized_value; DO(ConsumeAnyValue(full_type_name, message->GetDescriptor()->file()->pool(), @@ -389,7 +391,6 @@ class TextFormat::Parser::ParserImpl { string(prefix + full_type_name)); reflection->SetString(message, any_value_field, serialized_value); return true; - // Fall through. } if (TryConsume("[")) { // Extension. @@ -656,7 +657,7 @@ class TextFormat::Parser::ParserImpl { case FieldDescriptor::CPPTYPE_FLOAT: { double value; DO(ConsumeDouble(&value)); - SET_FIELD(Float, static_cast<float>(value)); + SET_FIELD(Float, io::SafeDoubleToFloat(value)); break; } @@ -1357,7 +1358,10 @@ string TextFormat::FieldValuePrinter::PrintDouble(double val) const { return SimpleDtoa(val); } string TextFormat::FieldValuePrinter::PrintString(const string& val) const { - return StrCat("\"", CEscape(val), "\""); + string printed("\""); + CEscapeAndAppend(val, &printed); + printed.push_back('\"'); + return printed; } string TextFormat::FieldValuePrinter::PrintBytes(const string& val) const { return PrintString(val); @@ -1423,7 +1427,8 @@ TextFormat::Printer::Printer() use_short_repeated_primitives_(false), hide_unknown_fields_(false), print_message_fields_in_index_order_(false), - expand_any_(false) { + expand_any_(false), + truncate_string_field_longer_than_(0LL) { SetUseUtf8StringEscaping(false); } @@ -1775,11 +1780,21 @@ 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<int64>(value.size())); + } + string truncated_value(value.substr(0, size) + "...<truncated>..."); + const string* value_to_print = &value; + if (size < value.size()) { + value_to_print = &truncated_value; + } if (field->type() == FieldDescriptor::TYPE_STRING) { - generator.Print(printer->PrintString(value)); + generator.Print(printer->PrintString(*value_to_print)); } else { GOOGLE_DCHECK_EQ(field->type(), FieldDescriptor::TYPE_BYTES); - generator.Print(printer->PrintBytes(value)); + generator.Print(printer->PrintBytes(*value_to_print)); } break; } @@ -1926,14 +1941,10 @@ void TextFormat::Printer::PrintUnknownFields( } else { // This field is not parseable as a Message. // So it is probably just a plain string. - generator.Print(": \""); - generator.Print(CEscape(value)); - generator.Print("\""); - if (single_line_mode_) { - generator.Print(" "); - } else { - generator.Print("\n"); - } + string printed(": \""); + CEscapeAndAppend(value, &printed); + printed.append(single_line_mode_ ? "\" " : "\"\n"); + generator.Print(printed); } break; } diff --git a/src/google/protobuf/text_format.h b/src/google/protobuf/text_format.h index 6717aecd..ef3d4a8f 100644 --- a/src/google/protobuf/text_format.h +++ b/src/google/protobuf/text_format.h @@ -219,6 +219,18 @@ class LIBPROTOBUF_EXPORT TextFormat { expand_any_ = expand; } + // If non-zero, we truncate all string fields that are longer than this + // threshold. This is useful when the proto message has very long strings, + // e.g., dump of encoded image file. + // + // NOTE(hfgong): Setting a non-zero value breaks round-trip safe + // property of TextFormat::Printer. That is, from the printed message, we + // cannot fully recover the original string field any more. + void SetTruncateStringFieldLongerThan( + const int64 truncate_string_field_longer_than) { + truncate_string_field_longer_than_ = truncate_string_field_longer_than; + } + // Register a custom field-specific FieldValuePrinter for fields // with a particular FieldDescriptor. // Returns "true" if the registration succeeded, or "false", if there is @@ -286,6 +298,8 @@ class LIBPROTOBUF_EXPORT TextFormat { bool expand_any_; + int64 truncate_string_field_longer_than_; + google::protobuf::scoped_ptr<const FieldValuePrinter> default_field_value_printer_; typedef map<const FieldDescriptor*, const FieldValuePrinter*> CustomPrinterMap; diff --git a/src/google/protobuf/timestamp.pb.cc b/src/google/protobuf/timestamp.pb.cc index f0b09195..c1c4402c 100644 --- a/src/google/protobuf/timestamp.pb.cc +++ b/src/google/protobuf/timestamp.pb.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/port.h> #include <google/protobuf/stubs/once.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/wire_format_lite_inl.h> @@ -82,9 +83,9 @@ 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(\005BQ\n\023com.google.protobufB\016" - "TimestampProtoP\001\240\001\001\242\002\003GPB\252\002\036Google.Proto" - "buf.WellKnownTypesb\006proto3", 186); + "\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); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/timestamp.proto", &protobuf_RegisterTypes); Timestamp::default_instance_ = new Timestamp(); @@ -122,6 +123,14 @@ Timestamp::Timestamp() // @@protoc_insertion_point(constructor:google.protobuf.Timestamp) } +Timestamp::Timestamp(::google::protobuf::Arena* arena) + : ::google::protobuf::Message(), + _internal_metadata_(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:google.protobuf.Timestamp) +} + void Timestamp::InitAsDefaultInstance() { _is_default_instance_ = true; } @@ -147,10 +156,20 @@ Timestamp::~Timestamp() { } void Timestamp::SharedDtor() { + if (GetArenaNoVirtual() != NULL) { + return; + } + if (this != default_instance_) { } } +void Timestamp::ArenaDtor(void* object) { + Timestamp* _this = reinterpret_cast< Timestamp* >(object); + (void)_this; +} +void Timestamp::RegisterArenaDtor(::google::protobuf::Arena* arena) { +} void Timestamp::SetCachedSize(int size) const { GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = size; @@ -169,11 +188,7 @@ const Timestamp& Timestamp::default_instance() { Timestamp* Timestamp::default_instance_ = NULL; Timestamp* Timestamp::New(::google::protobuf::Arena* arena) const { - Timestamp* n = new Timestamp; - if (arena != NULL) { - arena->Own(n); - } - return n; + return ::google::protobuf::Arena::CreateMessage<Timestamp>(arena); } void Timestamp::Clear() { @@ -349,6 +364,18 @@ bool Timestamp::IsInitialized() const { void Timestamp::Swap(Timestamp* other) { if (other == this) return; + if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) { + InternalSwap(other); + } else { + Timestamp temp; + temp.MergeFrom(*this); + CopyFrom(*other); + other->CopyFrom(temp); + } +} +void Timestamp::UnsafeArenaSwap(Timestamp* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual()); InternalSwap(other); } void Timestamp::InternalSwap(Timestamp* other) { diff --git a/src/google/protobuf/timestamp.pb.h b/src/google/protobuf/timestamp.pb.h index 85fc1242..7bf62597 100644 --- a/src/google/protobuf/timestamp.pb.h +++ b/src/google/protobuf/timestamp.pb.h @@ -53,9 +53,14 @@ class LIBPROTOBUF_EXPORT Timestamp : public ::google::protobuf::Message { return *this; } + inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); } + inline void* GetMaybeArenaPointer() const { + return MaybeArenaPtr(); + } static const ::google::protobuf::Descriptor* descriptor(); static const Timestamp& default_instance(); + void UnsafeArenaSwap(Timestamp* other); void Swap(Timestamp* other); // implements Message ---------------------------------------------- @@ -82,6 +87,11 @@ class LIBPROTOBUF_EXPORT Timestamp : public ::google::protobuf::Message { void SharedDtor(); void SetCachedSize(int size) const; void InternalSwap(Timestamp* other); + protected: + explicit Timestamp(::google::protobuf::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::google::protobuf::Arena* arena); private: inline ::google::protobuf::Arena* GetArenaNoVirtual() const { return _internal_metadata_.arena(); @@ -113,6 +123,9 @@ class LIBPROTOBUF_EXPORT Timestamp : public ::google::protobuf::Message { private: ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; bool _is_default_instance_; ::google::protobuf::int64 seconds_; ::google::protobuf::int32 nanos_; diff --git a/src/google/protobuf/timestamp.proto b/src/google/protobuf/timestamp.proto index 06b60e6f..b51fc3fa 100644 --- a/src/google/protobuf/timestamp.proto +++ b/src/google/protobuf/timestamp.proto @@ -27,15 +27,17 @@ // 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 google.protobuf; -option java_generate_equals_and_hash = true; -option java_multiple_files = true; -option java_outer_classname = "TimestampProto"; -option java_package = "com.google.protobuf"; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TimestampProto"; +option java_multiple_files = true; +option java_generate_equals_and_hash = true; option objc_class_prefix = "GPB"; // A Timestamp represents a point in time independent of any time zone @@ -92,6 +94,7 @@ option objc_class_prefix = "GPB"; // nanos = int((now - seconds) * 10**9) // timestamp = Timestamp(seconds=seconds, nanos=nanos) // +// message Timestamp { // Represents seconds of UTC time since Unix epoch diff --git a/src/google/protobuf/type.pb.cc b/src/google/protobuf/type.pb.cc index 5792dff2..7b47b3bd 100644 --- a/src/google/protobuf/type.pb.cc +++ b/src/google/protobuf/type.pb.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/port.h> #include <google/protobuf/stubs/once.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/wire_format_lite_inl.h> @@ -70,7 +71,7 @@ void protobuf_AssignDesc_google_2fprotobuf_2ftype_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Type, _internal_metadata_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Type, _is_default_instance_)); Field_descriptor_ = file->message_type(1); - static const int Field_offsets_[9] = { + static const int Field_offsets_[10] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, kind_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, cardinality_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, number_), @@ -80,6 +81,7 @@ void protobuf_AssignDesc_google_2fprotobuf_2ftype_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, packed_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, options_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, json_name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Field, default_value_), }; Field_reflection_ = ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( @@ -203,37 +205,37 @@ void protobuf_AddDesc_google_2fprotobuf_2ftype_2eproto() { "\030\004 \003(\0132\027.google.protobuf.Option\0226\n\016sourc" "e_context\030\005 \001(\0132\036.google.protobuf.Source" "Context\022\'\n\006syntax\030\006 \001(\0162\027.google.protobu" - "f.Syntax\"\276\005\n\005Field\022)\n\004kind\030\001 \001(\0162\033.googl" + "f.Syntax\"\325\005\n\005Field\022)\n\004kind\030\001 \001(\0162\033.googl" "e.protobuf.Field.Kind\0227\n\013cardinality\030\002 \001" "(\0162\".google.protobuf.Field.Cardinality\022\016" "\n\006number\030\003 \001(\005\022\014\n\004name\030\004 \001(\t\022\020\n\010type_url" "\030\006 \001(\t\022\023\n\013oneof_index\030\007 \001(\005\022\016\n\006packed\030\010 " "\001(\010\022(\n\007options\030\t \003(\0132\027.google.protobuf.O" - "ption\022\021\n\tjson_name\030\n \001(\t\"\310\002\n\004Kind\022\020\n\014TYP" - "E_UNKNOWN\020\000\022\017\n\013TYPE_DOUBLE\020\001\022\016\n\nTYPE_FLO" - "AT\020\002\022\016\n\nTYPE_INT64\020\003\022\017\n\013TYPE_UINT64\020\004\022\016\n" - "\nTYPE_INT32\020\005\022\020\n\014TYPE_FIXED64\020\006\022\020\n\014TYPE_" - "FIXED32\020\007\022\r\n\tTYPE_BOOL\020\010\022\017\n\013TYPE_STRING\020" - "\t\022\016\n\nTYPE_GROUP\020\n\022\020\n\014TYPE_MESSAGE\020\013\022\016\n\nT" - "YPE_BYTES\020\014\022\017\n\013TYPE_UINT32\020\r\022\r\n\tTYPE_ENU" - "M\020\016\022\021\n\rTYPE_SFIXED32\020\017\022\021\n\rTYPE_SFIXED64\020" - "\020\022\017\n\013TYPE_SINT32\020\021\022\017\n\013TYPE_SINT64\020\022\"t\n\013C" - "ardinality\022\027\n\023CARDINALITY_UNKNOWN\020\000\022\030\n\024C" - "ARDINALITY_OPTIONAL\020\001\022\030\n\024CARDINALITY_REQ" - "UIRED\020\002\022\030\n\024CARDINALITY_REPEATED\020\003\"\316\001\n\004En" - "um\022\014\n\004name\030\001 \001(\t\022-\n\tenumvalue\030\002 \003(\0132\032.go" - "ogle.protobuf.EnumValue\022(\n\007options\030\003 \003(\013" - "2\027.google.protobuf.Option\0226\n\016source_cont" - "ext\030\004 \001(\0132\036.google.protobuf.SourceContex" - "t\022\'\n\006syntax\030\005 \001(\0162\027.google.protobuf.Synt" - "ax\"S\n\tEnumValue\022\014\n\004name\030\001 \001(\t\022\016\n\006number\030" - "\002 \001(\005\022(\n\007options\030\003 \003(\0132\027.google.protobuf" - ".Option\";\n\006Option\022\014\n\004name\030\001 \001(\t\022#\n\005value" - "\030\002 \001(\0132\024.google.protobuf.Any*.\n\006Syntax\022\021" - "\n\rSYNTAX_PROTO2\020\000\022\021\n\rSYNTAX_PROTO3\020\001BL\n\023" - "com.google.protobufB\tTypeProtoP\001\240\001\001\242\002\003GP" - "B\252\002\036Google.Protobuf.WellKnownTypesb\006prot" - "o3", 1522); + "ption\022\021\n\tjson_name\030\n \001(\t\022\025\n\rdefault_valu" + "e\030\013 \001(\t\"\310\002\n\004Kind\022\020\n\014TYPE_UNKNOWN\020\000\022\017\n\013TY" + "PE_DOUBLE\020\001\022\016\n\nTYPE_FLOAT\020\002\022\016\n\nTYPE_INT6" + "4\020\003\022\017\n\013TYPE_UINT64\020\004\022\016\n\nTYPE_INT32\020\005\022\020\n\014" + "TYPE_FIXED64\020\006\022\020\n\014TYPE_FIXED32\020\007\022\r\n\tTYPE" + "_BOOL\020\010\022\017\n\013TYPE_STRING\020\t\022\016\n\nTYPE_GROUP\020\n" + "\022\020\n\014TYPE_MESSAGE\020\013\022\016\n\nTYPE_BYTES\020\014\022\017\n\013TY" + "PE_UINT32\020\r\022\r\n\tTYPE_ENUM\020\016\022\021\n\rTYPE_SFIXE" + "D32\020\017\022\021\n\rTYPE_SFIXED64\020\020\022\017\n\013TYPE_SINT32\020" + "\021\022\017\n\013TYPE_SINT64\020\022\"t\n\013Cardinality\022\027\n\023CAR" + "DINALITY_UNKNOWN\020\000\022\030\n\024CARDINALITY_OPTION" + "AL\020\001\022\030\n\024CARDINALITY_REQUIRED\020\002\022\030\n\024CARDIN" + "ALITY_REPEATED\020\003\"\316\001\n\004Enum\022\014\n\004name\030\001 \001(\t\022" + "-\n\tenumvalue\030\002 \003(\0132\032.google.protobuf.Enu" + "mValue\022(\n\007options\030\003 \003(\0132\027.google.protobu" + "f.Option\0226\n\016source_context\030\004 \001(\0132\036.googl" + "e.protobuf.SourceContext\022\'\n\006syntax\030\005 \001(\016" + "2\027.google.protobuf.Syntax\"S\n\tEnumValue\022\014" + "\n\004name\030\001 \001(\t\022\016\n\006number\030\002 \001(\005\022(\n\007options\030" + "\003 \003(\0132\027.google.protobuf.Option\";\n\006Option" + "\022\014\n\004name\030\001 \001(\t\022#\n\005value\030\002 \001(\0132\024.google.p" + "rotobuf.Any*.\n\006Syntax\022\021\n\rSYNTAX_PROTO2\020\000" + "\022\021\n\rSYNTAX_PROTO3\020\001BL\n\023com.google.protob" + "ufB\tTypeProtoP\001\240\001\001\242\002\003GPB\252\002\036Google.Protob" + "uf.WellKnownTypesb\006proto3", 1545); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/type.proto", &protobuf_RegisterTypes); Type::default_instance_ = new Type(); @@ -1026,6 +1028,7 @@ const int Field::kOneofIndexFieldNumber; const int Field::kPackedFieldNumber; const int Field::kOptionsFieldNumber; const int Field::kJsonNameFieldNumber; +const int Field::kDefaultValueFieldNumber; #endif // !defined(_MSC_VER) || _MSC_VER >= 1900 Field::Field() @@ -1058,6 +1061,7 @@ void Field::SharedCtor() { oneof_index_ = 0; packed_ = false; json_name_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + default_value_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } Field::~Field() { @@ -1069,6 +1073,7 @@ void Field::SharedDtor() { name_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); type_url_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); json_name_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + default_value_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); if (this != default_instance_) { } } @@ -1113,6 +1118,7 @@ void Field::Clear() { type_url_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); packed_ = false; json_name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + default_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); #undef ZR_HELPER_ #undef ZR_ @@ -1270,6 +1276,23 @@ bool Field::MergePartialFromCodedStream( } else { goto handle_unusual; } + if (input->ExpectTag(90)) goto parse_default_value; + break; + } + + // optional string default_value = 11; + case 11: { + if (tag == 90) { + parse_default_value: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_default_value())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->default_value().data(), this->default_value().length(), + ::google::protobuf::internal::WireFormatLite::PARSE, + "google.protobuf.Field.default_value")); + } else { + goto handle_unusual; + } if (input->ExpectAtEnd()) goto success; break; } @@ -1361,6 +1384,16 @@ void Field::SerializeWithCachedSizes( 10, this->json_name(), output); } + // optional string default_value = 11; + if (this->default_value().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->default_value().data(), this->default_value().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "google.protobuf.Field.default_value"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( + 11, this->default_value(), output); + } + // @@protoc_insertion_point(serialize_end:google.protobuf.Field) } @@ -1434,6 +1467,17 @@ void Field::SerializeWithCachedSizes( 10, this->json_name(), target); } + // optional string default_value = 11; + if (this->default_value().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->default_value().data(), this->default_value().length(), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "google.protobuf.Field.default_value"); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 11, this->default_value(), target); + } + // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Field) return target; } @@ -1493,6 +1537,13 @@ int Field::ByteSize() const { this->json_name()); } + // optional string default_value = 11; + if (this->default_value().size() > 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->default_value()); + } + // repeated .google.protobuf.Option options = 9; total_size += 1 * this->options_size(); for (int i = 0; i < this->options_size(); i++) { @@ -1549,6 +1600,10 @@ void Field::MergeFrom(const Field& from) { json_name_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.json_name_); } + if (from.default_value().size() > 0) { + + default_value_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.default_value_); + } } void Field::CopyFrom(const ::google::protobuf::Message& from) { @@ -1582,6 +1637,7 @@ void Field::InternalSwap(Field* other) { std::swap(packed_, other->packed_); options_.UnsafeArenaSwap(&other->options_); json_name_.Swap(&other->json_name_); + default_value_.Swap(&other->default_value_); _internal_metadata_.Swap(&other->_internal_metadata_); std::swap(_cached_size_, other->_cached_size_); } @@ -1826,6 +1882,49 @@ void Field::clear_json_name() { // @@protoc_insertion_point(field_set_allocated:google.protobuf.Field.json_name) } +// optional string default_value = 11; +void Field::clear_default_value() { + default_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + const ::std::string& Field::default_value() const { + // @@protoc_insertion_point(field_get:google.protobuf.Field.default_value) + return default_value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void Field::set_default_value(const ::std::string& value) { + + default_value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:google.protobuf.Field.default_value) +} + void Field::set_default_value(const char* value) { + + default_value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:google.protobuf.Field.default_value) +} + void Field::set_default_value(const char* value, size_t size) { + + default_value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast<const char*>(value), size)); + // @@protoc_insertion_point(field_set_pointer:google.protobuf.Field.default_value) +} + ::std::string* Field::mutable_default_value() { + + // @@protoc_insertion_point(field_mutable:google.protobuf.Field.default_value) + return default_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + ::std::string* Field::release_default_value() { + + return default_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + void Field::set_allocated_default_value(::std::string* default_value) { + if (default_value != NULL) { + + } else { + + } + default_value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), default_value); + // @@protoc_insertion_point(field_set_allocated:google.protobuf.Field.default_value) +} + #endif // PROTOBUF_INLINE_NOT_IN_HEADERS // =================================================================== diff --git a/src/google/protobuf/type.pb.h b/src/google/protobuf/type.pb.h index 2533eb4e..76fe8a65 100644 --- a/src/google/protobuf/type.pb.h +++ b/src/google/protobuf/type.pb.h @@ -471,6 +471,17 @@ class LIBPROTOBUF_EXPORT Field : public ::google::protobuf::Message { ::std::string* release_json_name(); void set_allocated_json_name(::std::string* json_name); + // optional string default_value = 11; + void clear_default_value(); + static const int kDefaultValueFieldNumber = 11; + const ::std::string& default_value() const; + void set_default_value(const ::std::string& value); + void set_default_value(const char* value); + void set_default_value(const char* value, size_t size); + ::std::string* mutable_default_value(); + ::std::string* release_default_value(); + void set_allocated_default_value(::std::string* default_value); + // @@protoc_insertion_point(class_scope:google.protobuf.Field) private: @@ -484,6 +495,7 @@ class LIBPROTOBUF_EXPORT Field : public ::google::protobuf::Message { ::google::protobuf::internal::ArenaStringPtr type_url_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::Option > options_; ::google::protobuf::internal::ArenaStringPtr json_name_; + ::google::protobuf::internal::ArenaStringPtr default_value_; bool packed_; mutable int _cached_size_; friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2ftype_2eproto(); @@ -1264,6 +1276,49 @@ inline void Field::set_allocated_json_name(::std::string* json_name) { // @@protoc_insertion_point(field_set_allocated:google.protobuf.Field.json_name) } +// optional string default_value = 11; +inline void Field::clear_default_value() { + default_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& Field::default_value() const { + // @@protoc_insertion_point(field_get:google.protobuf.Field.default_value) + return default_value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void Field::set_default_value(const ::std::string& value) { + + default_value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:google.protobuf.Field.default_value) +} +inline void Field::set_default_value(const char* value) { + + default_value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:google.protobuf.Field.default_value) +} +inline void Field::set_default_value(const char* value, size_t size) { + + default_value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast<const char*>(value), size)); + // @@protoc_insertion_point(field_set_pointer:google.protobuf.Field.default_value) +} +inline ::std::string* Field::mutable_default_value() { + + // @@protoc_insertion_point(field_mutable:google.protobuf.Field.default_value) + return default_value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* Field::release_default_value() { + + return default_value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void Field::set_allocated_default_value(::std::string* default_value) { + if (default_value != NULL) { + + } else { + + } + default_value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), default_value); + // @@protoc_insertion_point(field_set_allocated:google.protobuf.Field.default_value) +} + // ------------------------------------------------------------------- // Enum diff --git a/src/google/protobuf/type.proto b/src/google/protobuf/type.proto index 4df95762..1c9cf53d 100644 --- a/src/google/protobuf/type.proto +++ b/src/google/protobuf/type.proto @@ -27,6 +27,7 @@ // 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 google.protobuf; @@ -34,22 +35,22 @@ package google.protobuf; import "google/protobuf/any.proto"; import "google/protobuf/source_context.proto"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; option java_package = "com.google.protobuf"; option java_outer_classname = "TypeProto"; option java_multiple_files = true; option java_generate_equals_and_hash = true; -option csharp_namespace = "Google.Protobuf.WellKnownTypes"; option objc_class_prefix = "GPB"; -// A light-weight descriptor for a proto message type. +// A protocol buffer message type. message Type { // The fully qualified message name. string name = 1; // The list of fields. repeated Field fields = 2; - // The list of oneof definitions. - repeated string oneofs = 3; // The list of oneofs declared in this Type - // The proto options. + // The list of types appearing in `oneof` definitions in this type. + repeated string oneofs = 3; + // The protocol buffer options. repeated Option options = 4; // The source context. SourceContext source_context = 5; @@ -57,9 +58,9 @@ message Type { Syntax syntax = 6; } -// Field represents a single field of a message type. +// A single field of a message type. message Field { - // Kind represents a basic field type. + // Basic field types. enum Kind { // Field type unknown. TYPE_UNKNOWN = 0; @@ -81,7 +82,7 @@ message Field { TYPE_BOOL = 8; // Field type string. TYPE_STRING = 9; - // Field type group (deprecated proto2 type) + // Field type group. Proto2 syntax only, and deprecated. TYPE_GROUP = 10; // Field type message. TYPE_MESSAGE = 11; @@ -101,38 +102,40 @@ message Field { TYPE_SINT64 = 18; }; - // Cardinality represents whether a field is optional, required, or - // repeated. + // Whether a field is optional, required, or repeated. enum Cardinality { - // The field cardinality is unknown. Typically an error condition. + // For fields with unknown cardinality. CARDINALITY_UNKNOWN = 0; // For optional fields. CARDINALITY_OPTIONAL = 1; - // For required fields. Not used for proto3. + // For required fields. Proto2 syntax only. CARDINALITY_REQUIRED = 2; // For repeated fields. CARDINALITY_REPEATED = 3; }; - // The field kind. + // The field type. Kind kind = 1; - // The field cardinality, i.e. optional/required/repeated. + // The field cardinality. Cardinality cardinality = 2; - // The proto field number. + // The field number. int32 number = 3; // The field name. string name = 4; - // The type URL (without the scheme) when the type is MESSAGE or ENUM, - // such as `type.googleapis.com/google.protobuf.Empty`. + // The field type URL, without the scheme, for message or enumeration + // types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`. string type_url = 6; - // Index in Type.oneofs. Starts at 1. Zero means no oneof mapping. + // The index of the field type in `Type.oneofs`, for message or enumeration + // types. The first type has index 1; zero means the type is not in the list. int32 oneof_index = 7; // Whether to use alternative packed wire representation. bool packed = 8; - // The proto options. + // The protocol buffer options. repeated Option options = 9; - // The JSON name for this field. + // The field JSON name. string json_name = 10; + // The string value of the default value of this field. Proto2 syntax only. + string default_value = 11; } // Enum type definition. @@ -141,7 +144,7 @@ message Enum { string name = 1; // Enum value definitions. repeated EnumValue enumvalue = 2; - // Proto options for the enum type. + // Protocol buffer options. repeated Option options = 3; // The source context. SourceContext source_context = 4; @@ -155,22 +158,23 @@ message EnumValue { string name = 1; // Enum value number. int32 number = 2; - // Proto options for the enum value. + // Protocol buffer options. repeated Option options = 3; } -// Proto option attached to messages/fields/enums etc. +// A protocol buffer option, which can be attached to a message, field, +// enumeration, etc. message Option { - // Proto option name. + // The option's name. For example, `"java_package"`. string name = 1; - // Proto option value. + // The option's value. For example, `"com.google.protobuf"`. Any value = 2; } -// Syntax specifies the syntax in which a service element was defined. +// The syntax in which a protocol buffer element is defined. enum Syntax { - // Syntax "proto2" + // Syntax `proto2`. SYNTAX_PROTO2 = 0; - // Syntax "proto3" + // Syntax `proto3`. SYNTAX_PROTO3 = 1; } diff --git a/src/google/protobuf/unittest_custom_options.proto b/src/google/protobuf/unittest_custom_options.proto index d4d6e869..4cc0e362 100644 --- a/src/google/protobuf/unittest_custom_options.proto +++ b/src/google/protobuf/unittest_custom_options.proto @@ -392,3 +392,30 @@ message NestedOptionType { optional int32 nested_extension = 7912573 [(field_opt2) = 1005]; } } + +// Custom message option that has a required enum field. +// WARNING: this is strongly discouraged! +message OldOptionType { + enum TestEnum { + OLD_VALUE = 0; + } + required TestEnum value = 1; +} + +// Updated version of the custom option above. +message NewOptionType { + enum TestEnum { + OLD_VALUE = 0; + NEW_VALUE = 1; + } + required TestEnum value = 1; +} + +extend google.protobuf.MessageOptions { + optional OldOptionType required_enum_opt = 106161807; +} + +// Test message using the "required_enum_opt" option defined above. +message TestMessageWithRequiredEnumOption { + option (required_enum_opt) = { value: OLD_VALUE }; +} diff --git a/src/google/protobuf/util/field_comparator.h b/src/google/protobuf/util/field_comparator.h index ee676265..8b83c69f 100644 --- a/src/google/protobuf/util/field_comparator.h +++ b/src/google/protobuf/util/field_comparator.h @@ -93,7 +93,7 @@ class LIBPROTOBUF_EXPORT FieldComparator { // Basic implementation of FieldComparator. Supports four modes of floating // point value comparison: exact, approximate using MathUtil::AlmostEqual -// method, and arbitrarilly precise using MathUtil::WithinFracionOrMargin. +// method, and arbitrarilly 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 d3d34602..23f7d51d 100644 --- a/src/google/protobuf/util/field_comparator_test.cc +++ b/src/google/protobuf/util/field_comparator_test.cc @@ -35,7 +35,12 @@ #include <google/protobuf/unittest.pb.h> #include <google/protobuf/descriptor.h> #include <google/protobuf/stubs/mathutil.h> - +// This gtest header is put after mathutil.h intentionally. We have to do +// this because mathutil.h includes mathlimits.h which requires cmath not +// being included to compile on some versions of gcc: +// https://github.com/google/protobuf/blob/818c5eee08840355d70d2f3bdf1a2f17986a5e70/src/google/protobuf/stubs/mathlimits.h#L48 +// and the opensource version gtest.h header includes cmath transitively +// somehow. #include <gtest/gtest.h> namespace google { diff --git a/src/google/protobuf/util/internal/datapiece.cc b/src/google/protobuf/util/internal/datapiece.cc index ea360798..b557429f 100644 --- a/src/google/protobuf/util/internal/datapiece.cc +++ b/src/google/protobuf/util/internal/datapiece.cc @@ -35,8 +35,8 @@ #include <google/protobuf/descriptor.h> #include <google/protobuf/util/internal/utility.h> #include <google/protobuf/stubs/strutil.h> -#include <google/protobuf/stubs/mathutil.h> #include <google/protobuf/stubs/mathlimits.h> +#include <google/protobuf/stubs/mathutil.h> namespace google { namespace protobuf { @@ -57,13 +57,8 @@ inline Status InvalidArgument(StringPiece value_str) { return Status(util::error::INVALID_ARGUMENT, value_str); } -// For general conversion between -// int32, int64, uint32, uint64, double and float -// except conversion between double and float. template <typename To, typename From> -StatusOr<To> NumberConvertAndCheck(From before) { - if (::google::protobuf::internal::is_same<From, To>::value) return before; - To after = static_cast<To>(before); +StatusOr<To> ValidateNumberConversion(To after, From before) { if (after == before && MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) { return after; @@ -76,6 +71,27 @@ StatusOr<To> NumberConvertAndCheck(From before) { } } +// For general conversion between +// int32, int64, uint32, uint64, double and float +// except conversion between double and float. +template <typename To, typename From> +StatusOr<To> NumberConvertAndCheck(From before) { + if (::google::protobuf::internal::is_same<From, To>::value) return before; + + To after = static_cast<To>(before); + return ValidateNumberConversion(after, before); +} + +// For conversion to integer types (int32, int64, uint32, uint64) from floating +// point types (double, float) only. +template <typename To, typename From> +StatusOr<To> FloatingPointToIntConvertAndCheck(From before) { + if (::google::protobuf::internal::is_same<From, To>::value) return before; + + To after = static_cast<To>(before); + return ValidateNumberConversion(after, before); +} + // For conversion between double and float only. template <typename To, typename From> StatusOr<To> FloatingPointConvertAndCheck(From before) { @@ -96,30 +112,50 @@ StatusOr<To> FloatingPointConvertAndCheck(From before) { } // namespace StatusOr<int32> DataPiece::ToInt32() const { - if (type_ == TYPE_STRING) { - return StringToNumber<int32>(safe_strto32); - } + if (type_ == TYPE_STRING) return StringToNumber<int32>(safe_strto32); + + if (type_ == TYPE_DOUBLE) + return FloatingPointToIntConvertAndCheck<int32, double>(double_); + + if (type_ == TYPE_FLOAT) + return FloatingPointToIntConvertAndCheck<int32, float>(float_); + return GenericConvert<int32>(); } StatusOr<uint32> DataPiece::ToUint32() const { - if (type_ == TYPE_STRING) { - return StringToNumber<uint32>(safe_strtou32); - } + if (type_ == TYPE_STRING) return StringToNumber<uint32>(safe_strtou32); + + if (type_ == TYPE_DOUBLE) + return FloatingPointToIntConvertAndCheck<uint32, double>(double_); + + if (type_ == TYPE_FLOAT) + return FloatingPointToIntConvertAndCheck<uint32, float>(float_); + return GenericConvert<uint32>(); } StatusOr<int64> DataPiece::ToInt64() const { - if (type_ == TYPE_STRING) { - return StringToNumber<int64>(safe_strto64); - } + if (type_ == TYPE_STRING) return StringToNumber<int64>(safe_strto64); + + if (type_ == TYPE_DOUBLE) + return FloatingPointToIntConvertAndCheck<int64, double>(double_); + + if (type_ == TYPE_FLOAT) + return FloatingPointToIntConvertAndCheck<int64, float>(float_); + return GenericConvert<int64>(); } StatusOr<uint64> DataPiece::ToUint64() const { - if (type_ == TYPE_STRING) { - return StringToNumber<uint64>(safe_strtou64); - } + if (type_ == TYPE_STRING) return StringToNumber<uint64>(safe_strtou64); + + if (type_ == TYPE_DOUBLE) + return FloatingPointToIntConvertAndCheck<uint64, double>(double_); + + if (type_ == TYPE_FLOAT) + return FloatingPointToIntConvertAndCheck<uint64, float>(float_); + return GenericConvert<uint64>(); } diff --git a/src/google/protobuf/util/internal/datapiece.h b/src/google/protobuf/util/internal/datapiece.h index 2ab3fa88..f22bfe70 100644 --- a/src/google/protobuf/util/internal/datapiece.h +++ b/src/google/protobuf/util/internal/datapiece.h @@ -98,7 +98,8 @@ class LIBPROTOBUF_EXPORT DataPiece { static DataPiece NullData() { return DataPiece(TYPE_NULL, 0); } - virtual ~DataPiece() {} + virtual ~DataPiece() { + } // Accessors Type type() const { return type_; } diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.cc b/src/google/protobuf/util/internal/default_value_objectwriter.cc index 97b248ff..a63e560d 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter.cc +++ b/src/google/protobuf/util/internal/default_value_objectwriter.cc @@ -33,6 +33,7 @@ #include <google/protobuf/stubs/hash.h> #include <google/protobuf/util/internal/constants.h> +#include <google/protobuf/util/internal/utility.h> #include <google/protobuf/stubs/map_util.h> namespace google { @@ -42,13 +43,25 @@ using util::Status; using util::StatusOr; namespace converter { +namespace { +// Helper function to convert string value to given data type by calling the +// passed converter function on the DataPiece created from "value" argument. +// If value is empty or if conversion fails, the default_value is returned. +template <typename T> +T ConvertTo(StringPiece value, StatusOr<T> (DataPiece::*converter_fn)() const, + T default_value) { + if (value.empty()) return default_value; + StatusOr<T> result = (DataPiece(value).*converter_fn)(); + return result.ok() ? result.ValueOrDie() : default_value; +} +} // namespace + DefaultValueObjectWriter::DefaultValueObjectWriter( TypeResolver* type_resolver, const google::protobuf::Type& type, ObjectWriter* ow) : typeinfo_(TypeInfo::NewTypeInfo(type_resolver)), own_typeinfo_(true), type_(type), - disable_normalize_(false), current_(NULL), root_(NULL), ow_(ow) {} @@ -165,12 +178,6 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::RenderNull( return this; } -DefaultValueObjectWriter* -DefaultValueObjectWriter::DisableCaseNormalizationForNextKey() { - disable_normalize_ = true; - return this; -} - DefaultValueObjectWriter::Node::Node(const string& name, const google::protobuf::Type* type, NodeKind kind, const DataPiece& data, @@ -178,7 +185,6 @@ DefaultValueObjectWriter::Node::Node(const string& name, : name_(name), type_(type), kind_(kind), - disable_normalize_(false), is_any_(false), data_(data), is_placeholder_(is_placeholder) {} @@ -198,10 +204,6 @@ DefaultValueObjectWriter::Node* DefaultValueObjectWriter::Node::FindChild( } void DefaultValueObjectWriter::Node::WriteTo(ObjectWriter* ow) { - if (disable_normalize_) { - ow->DisableCaseNormalizationForNextKey(); - } - if (kind_ == PRIMITIVE) { ObjectWriter::RenderDataPieceTo(data_, name_, ow); return; @@ -324,6 +326,7 @@ void DefaultValueObjectWriter::Node::PopulateChildren( } } } + if (!is_map && field.cardinality() == google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) { @@ -336,11 +339,11 @@ void DefaultValueObjectWriter::Node::PopulateChildren( // If the child field is of primitive type, sets its data to the default // value of its type. - google::protobuf::scoped_ptr<Node> child( - new Node(field.json_name(), field_type, kind, - kind == PRIMITIVE ? CreateDefaultDataPieceForField(field) - : DataPiece::NullData(), - true)); + google::protobuf::scoped_ptr<Node> child(new Node( + field.json_name(), field_type, kind, + kind == PRIMITIVE ? CreateDefaultDataPieceForField(field, typeinfo) + : DataPiece::NullData(), + true)); new_children.push_back(child.release()); } // Adds all leftover nodes in children_ to the beginning of new_child. @@ -363,41 +366,68 @@ 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()); + + const google::protobuf::Enum* enum_type = + typeinfo->GetEnumByTypeUrl(field.type_url()); + if (!enum_type) { + GOOGLE_LOG(WARNING) << "Could not find enum with type '" << field.type_url() + << "'"; + return DataPiece::NullData(); + } + // 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::NullData(); +} + DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField( - const google::protobuf::Field& field) { + const google::protobuf::Field& field, const TypeInfo* typeinfo) { switch (field.kind()) { case google::protobuf::Field_Kind_TYPE_DOUBLE: { - return DataPiece(static_cast<double>(0)); + return DataPiece(ConvertTo<double>( + field.default_value(), &DataPiece::ToDouble, static_cast<double>(0))); } case google::protobuf::Field_Kind_TYPE_FLOAT: { - return DataPiece(static_cast<float>(0)); + return DataPiece(ConvertTo<float>( + field.default_value(), &DataPiece::ToFloat, static_cast<float>(0))); } case google::protobuf::Field_Kind_TYPE_INT64: case google::protobuf::Field_Kind_TYPE_SINT64: case google::protobuf::Field_Kind_TYPE_SFIXED64: { - return DataPiece(static_cast<int64>(0)); + return DataPiece(ConvertTo<int64>( + field.default_value(), &DataPiece::ToInt64, static_cast<int64>(0))); } case google::protobuf::Field_Kind_TYPE_UINT64: case google::protobuf::Field_Kind_TYPE_FIXED64: { - return DataPiece(static_cast<uint64>(0)); + return DataPiece(ConvertTo<uint64>( + field.default_value(), &DataPiece::ToUint64, static_cast<uint64>(0))); } case google::protobuf::Field_Kind_TYPE_INT32: case google::protobuf::Field_Kind_TYPE_SINT32: case google::protobuf::Field_Kind_TYPE_SFIXED32: { - return DataPiece(static_cast<int32>(0)); + return DataPiece(ConvertTo<int32>( + field.default_value(), &DataPiece::ToInt32, static_cast<int32>(0))); } case google::protobuf::Field_Kind_TYPE_BOOL: { - return DataPiece(false); + return DataPiece( + ConvertTo<bool>(field.default_value(), &DataPiece::ToBool, false)); } case google::protobuf::Field_Kind_TYPE_STRING: { - return DataPiece(string()); + return DataPiece(field.default_value()); } case google::protobuf::Field_Kind_TYPE_BYTES: { - return DataPiece("", false); + return DataPiece(field.default_value(), false); } case google::protobuf::Field_Kind_TYPE_UINT32: case google::protobuf::Field_Kind_TYPE_FIXED32: { - return DataPiece(static_cast<uint32>(0)); + return DataPiece(ConvertTo<uint32>( + field.default_value(), &DataPiece::ToUint32, static_cast<uint32>(0))); + } + case google::protobuf::Field_Kind_TYPE_ENUM: { + return FindEnumDefault(field, typeinfo); } default: { return DataPiece::NullData(); } } @@ -408,7 +438,6 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( if (current_ == NULL) { root_.reset(new Node(name.ToString(), &type_, OBJECT, DataPiece::NullData(), false)); - root_->set_disable_normalize(GetAndResetDisableNormalize()); root_->PopulateChildren(typeinfo_); current_ = root_.get(); return this; @@ -428,7 +457,6 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( } child->set_is_placeholder(false); - child->set_disable_normalize(GetAndResetDisableNormalize()); if (child->kind() == OBJECT && child->number_of_children() == 0) { child->PopulateChildren(typeinfo_); } @@ -454,21 +482,18 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartList( if (current_ == NULL) { root_.reset( new Node(name.ToString(), &type_, LIST, DataPiece::NullData(), false)); - root_->set_disable_normalize(GetAndResetDisableNormalize()); current_ = root_.get(); return this; } MaybePopulateChildrenOfAny(current_); Node* child = current_->FindChild(name); if (child == NULL || child->kind() != LIST) { - GOOGLE_LOG(WARNING) << "Cannot find field '" << name << "'."; google::protobuf::scoped_ptr<Node> node( new Node(name.ToString(), NULL, LIST, DataPiece::NullData(), false)); child = node.get(); current_->AddChild(node.release()); } child->set_is_placeholder(false); - child->set_disable_normalize(GetAndResetDisableNormalize()); stack_.push(current_); current_ = child; @@ -526,7 +551,6 @@ void DefaultValueObjectWriter::RenderDataPiece(StringPiece name, } else { child->set_data(data); } - child->set_disable_normalize(GetAndResetDisableNormalize()); } } // namespace converter diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.h b/src/google/protobuf/util/internal/default_value_objectwriter.h index bcb526e8..695b9dd8 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter.h +++ b/src/google/protobuf/util/internal/default_value_objectwriter.h @@ -98,8 +98,6 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { virtual DefaultValueObjectWriter* RenderNull(StringPiece name); - virtual DefaultValueObjectWriter* DisableCaseNormalizationForNextKey(); - private: enum NodeKind { PRIMITIVE = 0, @@ -149,10 +147,6 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { void set_data(const DataPiece& data) { data_ = data; } - void set_disable_normalize(bool disable_normalize) { - disable_normalize_ = disable_normalize; - } - bool is_any() { return is_any_; } void set_is_any(bool is_any) { is_any_ = is_any; } @@ -176,8 +170,6 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { const google::protobuf::Type* type_; // The kind of this node. NodeKind kind_; - // Whether to disable case normalization of the name. - bool disable_normalize_; // Whether this is a node for "Any". bool is_any_; // The data of this node when it is a leaf node. @@ -201,16 +193,17 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // Creates a DataPiece containing the default value of the type of the field. static DataPiece CreateDefaultDataPieceForField( - const google::protobuf::Field& field); - - // Returns disable_normalize_ and reset it to false. - bool GetAndResetDisableNormalize() { - return disable_normalize_ ? (disable_normalize_ = false, true) : false; - } + const google::protobuf::Field& field, const TypeInfo* typeinfo); // Adds or replaces the data_ of a primitive child node. void RenderDataPiece(StringPiece name, const DataPiece& data); + // Returns the default enum value as a DataPiece, or the first enum value if + // there is no default. For proto3, where we cannot specify an explicit + // default, a zero value will always be returned. + static DataPiece FindEnumDefault(const google::protobuf::Field& field, + const TypeInfo* typeinfo); + // Type information for all the types used in the descriptor. Used to find // google::protobuf::Type of nested messages/enums. const TypeInfo* typeinfo_; @@ -221,8 +214,6 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // Holds copies of strings passed to RenderString. vector<string*> string_values_; - // Whether to disable case normalization of the next node. - bool disable_normalize_; // The current Node. Owned by its parents. Node* current_; // The root Node. diff --git a/src/google/protobuf/util/internal/default_value_objectwriter_test.cc b/src/google/protobuf/util/internal/default_value_objectwriter_test.cc index 237d0722..b7a537ab 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter_test.cc +++ b/src/google/protobuf/util/internal/default_value_objectwriter_test.cc @@ -43,21 +43,19 @@ namespace testing { using google::protobuf::testing::DefaultValueTest; -// Tests to cover some basic DefaultValueObjectWriter use cases. More tests are -// in the marshalling_test.cc and translator_integration_test.cc. -class DefaultValueObjectWriterTest +// Base class for setting up required state for running default values tests on +// different descriptors. +class BaseDefaultValueObjectWriterTest : public ::testing::TestWithParam<testing::TypeInfoSource> { protected: - DefaultValueObjectWriterTest() + explicit BaseDefaultValueObjectWriterTest(const Descriptor* descriptor) : helper_(GetParam()), mock_(), expects_(&mock_) { - helper_.ResetTypeInfo(DefaultValueTest::descriptor()); + helper_.ResetTypeInfo(descriptor); testing_.reset(helper_.NewDefaultValueWriter( - string(kTypeServiceBaseUrl) + "/" + - DefaultValueTest::descriptor()->full_name(), - &mock_)); + string(kTypeServiceBaseUrl) + "/" + descriptor->full_name(), &mock_)); } - virtual ~DefaultValueObjectWriterTest() {} + ~BaseDefaultValueObjectWriterTest() override {} TypeInfoTestHelper helper_; MockObjectWriter mock_; @@ -65,6 +63,15 @@ class DefaultValueObjectWriterTest google::protobuf::scoped_ptr<DefaultValueObjectWriter> testing_; }; +// Tests to cover some basic DefaultValueObjectWriter use cases. More tests are +// in the marshalling_test.cc and translator_integration_test.cc. +class DefaultValueObjectWriterTest : public BaseDefaultValueObjectWriterTest { + protected: + DefaultValueObjectWriterTest() + : BaseDefaultValueObjectWriterTest(DefaultValueTest::descriptor()) {} + ~DefaultValueObjectWriterTest() override {} +}; + INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, DefaultValueObjectWriterTest, ::testing::Values( @@ -74,6 +81,8 @@ TEST_P(DefaultValueObjectWriterTest, Empty) { // Set expectation expects_.StartObject("") ->RenderDouble("doubleValue", 0.0) + ->StartList("repeatedDouble") + ->EndList() ->RenderFloat("floatValue", 0.0) ->RenderInt64("int64Value", 0) ->RenderUint64("uint64Value", 0) @@ -82,6 +91,7 @@ TEST_P(DefaultValueObjectWriterTest, Empty) { ->RenderBool("boolValue", false) ->RenderString("stringValue", "") ->RenderBytes("bytesValue", "") + ->RenderString("enumValue", "ENUM_FIRST") ->EndObject(); // Actual testing @@ -92,6 +102,8 @@ TEST_P(DefaultValueObjectWriterTest, NonDefaultDouble) { // Set expectation expects_.StartObject("") ->RenderDouble("doubleValue", 1.0) + ->StartList("repeatedDouble") + ->EndList() ->RenderFloat("floatValue", 0.0) ->RenderInt64("int64Value", 0) ->RenderUint64("uint64Value", 0) @@ -99,6 +111,7 @@ TEST_P(DefaultValueObjectWriterTest, NonDefaultDouble) { ->RenderUint32("uint32Value", 0) ->RenderBool("boolValue", false) ->RenderString("stringValue", "") + ->RenderString("enumValue", "ENUM_FIRST") ->EndObject(); // Actual testing @@ -109,6 +122,8 @@ TEST_P(DefaultValueObjectWriterTest, ShouldRetainUnknownField) { // Set expectation expects_.StartObject("") ->RenderDouble("doubleValue", 1.0) + ->StartList("repeatedDouble") + ->EndList() ->RenderFloat("floatValue", 0.0) ->RenderInt64("int64Value", 0) ->RenderUint64("uint64Value", 0) @@ -120,6 +135,7 @@ TEST_P(DefaultValueObjectWriterTest, ShouldRetainUnknownField) { ->StartObject("unknownObject") ->RenderString("unknown", "def") ->EndObject() + ->RenderString("enumValue", "ENUM_FIRST") ->EndObject(); // Actual testing @@ -132,6 +148,7 @@ TEST_P(DefaultValueObjectWriterTest, ShouldRetainUnknownField) { ->EndObject(); } + } // namespace testing } // namespace converter } // namespace util diff --git a/src/google/protobuf/util/internal/error_listener.h b/src/google/protobuf/util/internal/error_listener.h index 2699684d..3f063936 100644 --- a/src/google/protobuf/util/internal/error_listener.h +++ b/src/google/protobuf/util/internal/error_listener.h @@ -31,11 +31,13 @@ #ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_ERROR_LISTENER_H__ #define GOOGLE_PROTOBUF_UTIL_CONVERTER_ERROR_LISTENER_H__ +#include <algorithm> #include <memory> #ifndef _SHARED_PTR_H #include <google/protobuf/stubs/shared_ptr.h> #endif #include <string> +#include <vector> #include <google/protobuf/stubs/callback.h> #include <google/protobuf/stubs/common.h> diff --git a/src/google/protobuf/util/internal/json_objectwriter_test.cc b/src/google/protobuf/util/internal/json_objectwriter_test.cc index dcd60601..9d820162 100644 --- a/src/google/protobuf/util/internal/json_objectwriter_test.cc +++ b/src/google/protobuf/util/internal/json_objectwriter_test.cc @@ -47,8 +47,7 @@ class JsonObjectWriterTest : public ::testing::Test { JsonObjectWriterTest() : str_stream_(new StringOutputStream(&output_)), out_stream_(new CodedOutputStream(str_stream_)), - ow_(NULL) { - } + ow_(NULL) {} virtual ~JsonObjectWriterTest() { delete ow_; @@ -64,36 +63,34 @@ class JsonObjectWriterTest : public ::testing::Test { TEST_F(JsonObjectWriterTest, EmptyRootObject) { ow_ = new JsonObjectWriter("", out_stream_); - ow_->StartObject("") - ->EndObject(); + ow_->StartObject("")->EndObject(); EXPECT_EQ("{}", output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, EmptyObject) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->RenderString("test", "value") - ->StartObject("empty") - ->EndObject() - ->EndObject(); + ->RenderString("test", "value") + ->StartObject("empty") + ->EndObject() + ->EndObject(); EXPECT_EQ("{\"test\":\"value\",\"empty\":{}}", output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, EmptyRootList) { ow_ = new JsonObjectWriter("", out_stream_); - ow_->StartList("") - ->EndList(); + ow_->StartList("")->EndList(); EXPECT_EQ("[]", output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, EmptyList) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->RenderString("test", "value") - ->StartList("empty") - ->EndList() - ->EndObject(); + ->RenderString("test", "value") + ->StartList("empty") + ->EndList() + ->EndObject(); EXPECT_EQ("{\"test\":\"value\",\"empty\":[]}", output_.substr(0, out_stream_->ByteCount())); } @@ -101,10 +98,10 @@ TEST_F(JsonObjectWriterTest, EmptyList) { TEST_F(JsonObjectWriterTest, ObjectInObject) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->StartObject("nested") - ->RenderString("field", "value") - ->EndObject() - ->EndObject(); + ->StartObject("nested") + ->RenderString("field", "value") + ->EndObject() + ->EndObject(); EXPECT_EQ("{\"nested\":{\"field\":\"value\"}}", output_.substr(0, out_stream_->ByteCount())); } @@ -112,10 +109,10 @@ TEST_F(JsonObjectWriterTest, ObjectInObject) { TEST_F(JsonObjectWriterTest, ListInObject) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->StartList("nested") - ->RenderString("", "value") - ->EndList() - ->EndObject(); + ->StartList("nested") + ->RenderString("", "value") + ->EndList() + ->EndObject(); EXPECT_EQ("{\"nested\":[\"value\"]}", output_.substr(0, out_stream_->ByteCount())); } @@ -123,10 +120,10 @@ TEST_F(JsonObjectWriterTest, ListInObject) { TEST_F(JsonObjectWriterTest, ObjectInList) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartList("") - ->StartObject("") - ->RenderString("field", "value") - ->EndObject() - ->EndList(); + ->StartObject("") + ->RenderString("field", "value") + ->EndObject() + ->EndList(); EXPECT_EQ("[{\"field\":\"value\"}]", output_.substr(0, out_stream_->ByteCount())); } @@ -134,10 +131,10 @@ TEST_F(JsonObjectWriterTest, ObjectInList) { TEST_F(JsonObjectWriterTest, ListInList) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartList("") - ->StartList("") - ->RenderString("", "value") - ->EndList() - ->EndList(); + ->StartList("") + ->RenderString("", "value") + ->EndList() + ->EndList(); EXPECT_EQ("[[\"value\"]]", output_.substr(0, out_stream_->ByteCount())); } @@ -156,14 +153,18 @@ TEST_F(JsonObjectWriterTest, RenderPrimitives) { ->EndObject(); EXPECT_EQ( "{\"bool\":true," - "\"double\":" + ValueAsString<double>(1.7976931348623157e+308) + "," - "\"float\":" + ValueAsString<float>(3.4028235e+38) + "," - "\"int\":-2147483648," - "\"long\":\"-9223372036854775808\"," - "\"bytes\":\"YWJyYWNhZGFicmE=\"," - "\"string\":\"string\"," - "\"emptybytes\":\"\"," - "\"emptystring\":\"\"}", + "\"double\":" + + ValueAsString<double>(std::numeric_limits<double>::max()) + + "," + "\"float\":" + + ValueAsString<float>(std::numeric_limits<float>::max()) + + "," + "\"int\":-2147483648," + "\"long\":\"-9223372036854775808\"," + "\"bytes\":\"YWJyYWNhZGFicmE=\"," + "\"string\":\"string\"," + "\"emptybytes\":\"\"," + "\"emptystring\":\"\"}", output_.substr(0, out_stream_->ByteCount())); } @@ -181,81 +182,83 @@ TEST_F(JsonObjectWriterTest, BytesEncodesAsNonWebSafeBase64) { TEST_F(JsonObjectWriterTest, PrettyPrintList) { ow_ = new JsonObjectWriter(" ", out_stream_); ow_->StartObject("") - ->StartList("items") - ->RenderString("", "item1") - ->RenderString("", "item2") - ->RenderString("", "item3") - ->EndList() - ->StartList("empty") - ->EndList() - ->EndObject(); - EXPECT_EQ("{\n" - " \"items\": [\n" - " \"item1\",\n" - " \"item2\",\n" - " \"item3\"\n" - " ],\n" - " \"empty\": []\n" - "}\n", - output_.substr(0, out_stream_->ByteCount())); + ->StartList("items") + ->RenderString("", "item1") + ->RenderString("", "item2") + ->RenderString("", "item3") + ->EndList() + ->StartList("empty") + ->EndList() + ->EndObject(); + EXPECT_EQ( + "{\n" + " \"items\": [\n" + " \"item1\",\n" + " \"item2\",\n" + " \"item3\"\n" + " ],\n" + " \"empty\": []\n" + "}\n", + output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, PrettyPrintObject) { ow_ = new JsonObjectWriter(" ", out_stream_); ow_->StartObject("") - ->StartObject("items") - ->RenderString("key1", "item1") - ->RenderString("key2", "item2") - ->RenderString("key3", "item3") - ->EndObject() - ->StartObject("empty") - ->EndObject() - ->EndObject(); - EXPECT_EQ("{\n" - " \"items\": {\n" - " \"key1\": \"item1\",\n" - " \"key2\": \"item2\",\n" - " \"key3\": \"item3\"\n" - " },\n" - " \"empty\": {}\n" - "}\n", - output_.substr(0, out_stream_->ByteCount())); + ->StartObject("items") + ->RenderString("key1", "item1") + ->RenderString("key2", "item2") + ->RenderString("key3", "item3") + ->EndObject() + ->StartObject("empty") + ->EndObject() + ->EndObject(); + EXPECT_EQ( + "{\n" + " \"items\": {\n" + " \"key1\": \"item1\",\n" + " \"key2\": \"item2\",\n" + " \"key3\": \"item3\"\n" + " },\n" + " \"empty\": {}\n" + "}\n", + output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, PrettyPrintEmptyObjectInEmptyList) { ow_ = new JsonObjectWriter(" ", out_stream_); ow_->StartObject("") - ->StartList("list") - ->StartObject("") - ->EndObject() - ->EndList() - ->EndObject(); - EXPECT_EQ("{\n" - " \"list\": [\n" - " {}\n" - " ]\n" - "}\n", - output_.substr(0, out_stream_->ByteCount())); + ->StartList("list") + ->StartObject("") + ->EndObject() + ->EndList() + ->EndObject(); + EXPECT_EQ( + "{\n" + " \"list\": [\n" + " {}\n" + " ]\n" + "}\n", + output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, PrettyPrintDoubleIndent) { ow_ = new JsonObjectWriter(" ", out_stream_); ow_->StartObject("") - ->RenderBool("bool", true) - ->RenderInt32("int", 42) - ->EndObject(); - EXPECT_EQ("{\n" - " \"bool\": true,\n" - " \"int\": 42\n" - "}\n", - output_.substr(0, out_stream_->ByteCount())); + ->RenderBool("bool", true) + ->RenderInt32("int", 42) + ->EndObject(); + EXPECT_EQ( + "{\n" + " \"bool\": true,\n" + " \"int\": 42\n" + "}\n", + output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, StringsEscapedAndEnclosedInDoubleQuotes) { ow_ = new JsonObjectWriter("", out_stream_); - ow_->StartObject("") - ->RenderString("string", "'<>&\\\"\r\n") - ->EndObject(); + ow_->StartObject("")->RenderString("string", "'<>&\\\"\r\n")->EndObject(); EXPECT_EQ("{\"string\":\"'\\u003c\\u003e&\\\\\\\"\\r\\n\"}", output_.substr(0, out_stream_->ByteCount())); } @@ -263,13 +266,13 @@ TEST_F(JsonObjectWriterTest, StringsEscapedAndEnclosedInDoubleQuotes) { TEST_F(JsonObjectWriterTest, Stringification) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->RenderDouble("double_nan", std::numeric_limits<double>::quiet_NaN()) - ->RenderFloat("float_nan", std::numeric_limits<float>::quiet_NaN()) - ->RenderDouble("double_pos", std::numeric_limits<double>::infinity()) - ->RenderFloat("float_pos", std::numeric_limits<float>::infinity()) - ->RenderDouble("double_neg", -std::numeric_limits<double>::infinity()) - ->RenderFloat("float_neg", -std::numeric_limits<float>::infinity()) - ->EndObject(); + ->RenderDouble("double_nan", std::numeric_limits<double>::quiet_NaN()) + ->RenderFloat("float_nan", std::numeric_limits<float>::quiet_NaN()) + ->RenderDouble("double_pos", std::numeric_limits<double>::infinity()) + ->RenderFloat("float_pos", std::numeric_limits<float>::infinity()) + ->RenderDouble("double_neg", -std::numeric_limits<double>::infinity()) + ->RenderFloat("float_neg", -std::numeric_limits<float>::infinity()) + ->EndObject(); EXPECT_EQ( "{\"double_nan\":\"NaN\"," "\"float_nan\":\"NaN\"," diff --git a/src/google/protobuf/util/internal/json_stream_parser.cc b/src/google/protobuf/util/internal/json_stream_parser.cc index a7ef7fe2..df916751 100644 --- a/src/google/protobuf/util/internal/json_stream_parser.cc +++ b/src/google/protobuf/util/internal/json_stream_parser.cc @@ -157,10 +157,10 @@ util::Status JsonStreamParser::FinishParse() { char* coerced = internal::UTF8CoerceToStructurallyValid(leftover_, utf8.get(), ' '); p_ = json_ = StringPiece(coerced, leftover_.size()); } else { + p_ = json_ = leftover_; if (!internal::IsStructurallyValidUTF8(leftover_)) { return ReportFailure("Encountered non UTF-8 code points."); } - p_ = json_ = leftover_; } // Parse the remainder in finishing mode, which reports errors for things like 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 c833ed1f..3414826e 100644 --- a/src/google/protobuf/util/internal/json_stream_parser_test.cc +++ b/src/google/protobuf/util/internal/json_stream_parser_test.cc @@ -348,6 +348,7 @@ TEST_F(JsonStreamParserTest, RejectNonUtf8WhenNotCoerced) { for (int i = 0; i <= json.length(); ++i) { DoErrorTest(json, i, "Encountered non UTF-8 code points."); } + DoErrorTest("\xFF{}", 0, "Encountered non UTF-8 code points."); } #ifndef _MSC_VER diff --git a/src/google/protobuf/util/internal/object_writer.h b/src/google/protobuf/util/internal/object_writer.h index 20bd3627..e695f45e 100644 --- a/src/google/protobuf/util/internal/object_writer.h +++ b/src/google/protobuf/util/internal/object_writer.h @@ -101,11 +101,6 @@ class LIBPROTOBUF_EXPORT ObjectWriter { // Renders a Null value. virtual ObjectWriter* RenderNull(StringPiece name) = 0; - // Disables case normalization. Any RenderTYPE call after calling this - // function will output the name field as-is. No normalization is attempted on - // it. This setting is reset immediately after the next RenderTYPE is called. - virtual ObjectWriter* DisableCaseNormalizationForNextKey() { return this; } - // Renders a DataPiece object to a ObjectWriter. static void RenderDataPieceTo(const DataPiece& data, StringPiece name, ObjectWriter* ow); diff --git a/src/google/protobuf/util/internal/proto_writer.cc b/src/google/protobuf/util/internal/proto_writer.cc new file mode 100644 index 00000000..47e0009e --- /dev/null +++ b/src/google/protobuf/util/internal/proto_writer.cc @@ -0,0 +1,744 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <google/protobuf/util/internal/proto_writer.h> + +#include <functional> +#include <stack> + +#include <google/protobuf/stubs/once.h> +#include <google/protobuf/stubs/time.h> +#include <google/protobuf/wire_format_lite.h> +#include <google/protobuf/util/internal/field_mask_utility.h> +#include <google/protobuf/util/internal/object_location_tracker.h> +#include <google/protobuf/util/internal/constants.h> +#include <google/protobuf/util/internal/utility.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/stubs/map_util.h> +#include <google/protobuf/stubs/statusor.h> + + +namespace google { +namespace protobuf { +namespace util { +namespace converter { + +using google::protobuf::internal::WireFormatLite; +using google::protobuf::io::CodedOutputStream; +using util::error::INVALID_ARGUMENT; +using util::Status; +using util::StatusOr; + + +ProtoWriter::ProtoWriter(TypeResolver* type_resolver, + const google::protobuf::Type& type, + strings::ByteSink* output, ErrorListener* listener) + : master_type_(type), + typeinfo_(TypeInfo::NewTypeInfo(type_resolver)), + own_typeinfo_(true), + done_(false), + element_(NULL), + size_insert_(), + output_(output), + buffer_(), + adapter_(&buffer_), + stream_(new CodedOutputStream(&adapter_)), + listener_(listener), + invalid_depth_(0), + tracker_(new ObjectLocationTracker()) {} + +ProtoWriter::ProtoWriter(const TypeInfo* typeinfo, + const google::protobuf::Type& type, + strings::ByteSink* output, ErrorListener* listener) + : master_type_(type), + typeinfo_(typeinfo), + own_typeinfo_(false), + done_(false), + element_(NULL), + size_insert_(), + output_(output), + buffer_(), + adapter_(&buffer_), + stream_(new CodedOutputStream(&adapter_)), + listener_(listener), + invalid_depth_(0), + tracker_(new ObjectLocationTracker()) {} + +ProtoWriter::~ProtoWriter() { + if (own_typeinfo_) { + delete typeinfo_; + } + if (element_ == NULL) return; + // Cleanup explicitly in order to avoid destructor stack overflow when input + // is deeply nested. + // Cast to BaseElement to avoid doing additional checks (like missing fields) + // during pop(). + google::protobuf::scoped_ptr<BaseElement> element( + static_cast<BaseElement*>(element_.get())->pop<BaseElement>()); + while (element != NULL) { + element.reset(element->pop<BaseElement>()); + } +} + +namespace { + +// Writes an INT32 field, including tag to the stream. +inline Status WriteInt32(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<int32> i32 = data.ToInt32(); + if (i32.ok()) { + WireFormatLite::WriteInt32(field_number, i32.ValueOrDie(), stream); + } + return i32.status(); +} + +// writes an SFIXED32 field, including tag, to the stream. +inline Status WriteSFixed32(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<int32> i32 = data.ToInt32(); + if (i32.ok()) { + WireFormatLite::WriteSFixed32(field_number, i32.ValueOrDie(), stream); + } + return i32.status(); +} + +// Writes an SINT32 field, including tag, to the stream. +inline Status WriteSInt32(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<int32> i32 = data.ToInt32(); + if (i32.ok()) { + WireFormatLite::WriteSInt32(field_number, i32.ValueOrDie(), stream); + } + return i32.status(); +} + +// Writes a FIXED32 field, including tag, to the stream. +inline Status WriteFixed32(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<uint32> u32 = data.ToUint32(); + if (u32.ok()) { + WireFormatLite::WriteFixed32(field_number, u32.ValueOrDie(), stream); + } + return u32.status(); +} + +// Writes a UINT32 field, including tag, to the stream. +inline Status WriteUInt32(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<uint32> u32 = data.ToUint32(); + if (u32.ok()) { + WireFormatLite::WriteUInt32(field_number, u32.ValueOrDie(), stream); + } + return u32.status(); +} + +// Writes an INT64 field, including tag, to the stream. +inline Status WriteInt64(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<int64> i64 = data.ToInt64(); + if (i64.ok()) { + WireFormatLite::WriteInt64(field_number, i64.ValueOrDie(), stream); + } + return i64.status(); +} + +// Writes an SFIXED64 field, including tag, to the stream. +inline Status WriteSFixed64(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<int64> i64 = data.ToInt64(); + if (i64.ok()) { + WireFormatLite::WriteSFixed64(field_number, i64.ValueOrDie(), stream); + } + return i64.status(); +} + +// Writes an SINT64 field, including tag, to the stream. +inline Status WriteSInt64(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<int64> i64 = data.ToInt64(); + if (i64.ok()) { + WireFormatLite::WriteSInt64(field_number, i64.ValueOrDie(), stream); + } + return i64.status(); +} + +// Writes a FIXED64 field, including tag, to the stream. +inline Status WriteFixed64(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<uint64> u64 = data.ToUint64(); + if (u64.ok()) { + WireFormatLite::WriteFixed64(field_number, u64.ValueOrDie(), stream); + } + return u64.status(); +} + +// Writes a UINT64 field, including tag, to the stream. +inline Status WriteUInt64(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<uint64> u64 = data.ToUint64(); + if (u64.ok()) { + WireFormatLite::WriteUInt64(field_number, u64.ValueOrDie(), stream); + } + return u64.status(); +} + +// Writes a DOUBLE field, including tag, to the stream. +inline Status WriteDouble(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<double> d = data.ToDouble(); + if (d.ok()) { + WireFormatLite::WriteDouble(field_number, d.ValueOrDie(), stream); + } + return d.status(); +} + +// Writes a FLOAT field, including tag, to the stream. +inline Status WriteFloat(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<float> f = data.ToFloat(); + if (f.ok()) { + WireFormatLite::WriteFloat(field_number, f.ValueOrDie(), stream); + } + return f.status(); +} + +// Writes a BOOL field, including tag, to the stream. +inline Status WriteBool(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<bool> b = data.ToBool(); + if (b.ok()) { + WireFormatLite::WriteBool(field_number, b.ValueOrDie(), stream); + } + return b.status(); +} + +// Writes a BYTES field, including tag, to the stream. +inline Status WriteBytes(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<string> c = data.ToBytes(); + if (c.ok()) { + WireFormatLite::WriteBytes(field_number, c.ValueOrDie(), stream); + } + return c.status(); +} + +// Writes a STRING field, including tag, to the stream. +inline Status WriteString(int field_number, const DataPiece& data, + CodedOutputStream* stream) { + StatusOr<string> s = data.ToString(); + if (s.ok()) { + WireFormatLite::WriteString(field_number, s.ValueOrDie(), stream); + } + return s.status(); +} + +// Writes an ENUM field, including tag, to the stream. +inline Status WriteEnum(int field_number, const DataPiece& data, + const google::protobuf::Enum* enum_type, + CodedOutputStream* stream) { + StatusOr<int> e = data.ToEnum(enum_type); + if (e.ok()) { + WireFormatLite::WriteEnum(field_number, e.ValueOrDie(), stream); + } + return e.status(); +} + +// Given a google::protobuf::Type, returns the set of all required fields. +std::set<const google::protobuf::Field*> GetRequiredFields( + const google::protobuf::Type& type) { + std::set<const google::protobuf::Field*> required; + for (int i = 0; i < type.fields_size(); i++) { + const google::protobuf::Field& field = type.fields(i); + if (field.cardinality() == + google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) { + required.insert(&field); + } + } + return required; +} + +} // namespace + +ProtoWriter::ProtoElement::ProtoElement(const TypeInfo* typeinfo, + const google::protobuf::Type& type, + ProtoWriter* enclosing) + : BaseElement(NULL), + ow_(enclosing), + parent_field_(NULL), + typeinfo_(typeinfo), + type_(type), + required_fields_(GetRequiredFields(type)), + size_index_(-1), + array_index_(-1) {} + +ProtoWriter::ProtoElement::ProtoElement(ProtoWriter::ProtoElement* parent, + const google::protobuf::Field* field, + const google::protobuf::Type& type, + bool is_list) + : BaseElement(parent), + ow_(this->parent()->ow_), + parent_field_(field), + typeinfo_(this->parent()->typeinfo_), + type_(type), + size_index_( + !is_list && field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE + ? ow_->size_insert_.size() + : -1), + array_index_(is_list ? 0 : -1) { + if (!is_list) { + if (ow_->IsRepeated(*field)) { + // Update array_index_ if it is an explicit list. + if (this->parent()->array_index_ >= 0) this->parent()->array_index_++; + } else { + this->parent()->RegisterField(field); + } + + if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) { + required_fields_ = GetRequiredFields(type_); + int start_pos = ow_->stream_->ByteCount(); + // length of serialized message is the final buffer position minus + // starting buffer position, plus length adjustments for size fields + // of any nested messages. We start with -start_pos here, so we only + // need to add the final buffer position to it at the end. + SizeInfo info = {start_pos, -start_pos}; + ow_->size_insert_.push_back(info); + } + } +} + +ProtoWriter::ProtoElement* ProtoWriter::ProtoElement::pop() { + // Calls the registered error listener for any required field(s) not yet + // seen. + for (set<const google::protobuf::Field*>::iterator it = + required_fields_.begin(); + it != required_fields_.end(); ++it) { + ow_->MissingField((*it)->name()); + } + // Computes the total number of proto bytes used by a message, also adjusts + // the size of all parent messages by the length of this size field. + // If size_index_ < 0, this is not a message, so no size field is added. + if (size_index_ >= 0) { + // Add the final buffer position to compute the total length of this + // serialized message. The stored value (before this addition) already + // contains the total length of the size fields of all nested messages + // minus the initial buffer position. + ow_->size_insert_[size_index_].size += ow_->stream_->ByteCount(); + // Calculate the length required to serialize the size field of the + // message, and propagate this additional size information upward to + // all enclosing messages. + int size = ow_->size_insert_[size_index_].size; + int length = CodedOutputStream::VarintSize32(size); + for (ProtoElement* e = parent(); e != NULL; e = e->parent()) { + // Only nested messages have size field, lists do not have size field. + if (e->size_index_ >= 0) { + ow_->size_insert_[e->size_index_].size += length; + } + } + } + return BaseElement::pop<ProtoElement>(); +} + +void ProtoWriter::ProtoElement::RegisterField( + const google::protobuf::Field* field) { + if (!required_fields_.empty() && + field->cardinality() == + google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) { + required_fields_.erase(field); + } +} + +string ProtoWriter::ProtoElement::ToString() const { + if (parent() == NULL) return ""; + string loc = parent()->ToString(); + if (!ow_->IsRepeated(*parent_field_) || + parent()->parent_field_ != parent_field_) { + string name = parent_field_->name(); + int i = 0; + while (i < name.size() && (ascii_isalnum(name[i]) || name[i] == '_')) ++i; + if (i > 0 && i == name.size()) { // safe field name + if (loc.empty()) { + loc = name; + } else { + StrAppend(&loc, ".", name); + } + } else { + StrAppend(&loc, "[\"", CEscape(name), "\"]"); + } + } + if (ow_->IsRepeated(*parent_field_) && array_index_ > 0) { + StrAppend(&loc, "[", array_index_ - 1, "]"); + } + return loc.empty() ? "." : loc; +} + +bool ProtoWriter::ProtoElement::IsOneofIndexTaken(int32 index) { + return ContainsKey(oneof_indices_, index); +} + +void ProtoWriter::ProtoElement::TakeOneofIndex(int32 index) { + InsertIfNotPresent(&oneof_indices_, index); +} + +void ProtoWriter::InvalidName(StringPiece unknown_name, StringPiece message) { + listener_->InvalidName(location(), ToSnakeCase(unknown_name), message); +} + +void ProtoWriter::InvalidValue(StringPiece type_name, StringPiece value) { + listener_->InvalidValue(location(), type_name, value); +} + +void ProtoWriter::MissingField(StringPiece missing_name) { + listener_->MissingField(location(), missing_name); +} + +ProtoWriter* ProtoWriter::StartObject(StringPiece name) { + // Starting the root message. Create the root ProtoElement and return. + if (element_ == NULL) { + if (!name.empty()) { + InvalidName(name, "Root element should not be named."); + } + element_.reset(new ProtoElement(typeinfo_, master_type_, this)); + return this; + } + + const google::protobuf::Field* field = NULL; + field = BeginNamed(name, false); + if (field == NULL) return this; + + // Check to see if this field is a oneof and that no oneof in that group has + // already been set. + if (!ValidOneof(*field, name)) { + ++invalid_depth_; + return this; + } + + const google::protobuf::Type* type = LookupType(field); + if (type == NULL) { + ++invalid_depth_; + InvalidName(name, + StrCat("Missing descriptor for field: ", field->type_url())); + return this; + } + + WriteTag(*field); + element_.reset(new ProtoElement(element_.release(), field, *type, false)); + return this; +} + +ProtoWriter* ProtoWriter::EndObject() { + if (invalid_depth_ > 0) { + --invalid_depth_; + return this; + } + + if (element_ != NULL) { + element_.reset(element_->pop()); + } + + + // If ending the root element, + // then serialize the full message with calculated sizes. + if (element_ == NULL) { + WriteRootMessage(); + } + return this; +} + +ProtoWriter* ProtoWriter::StartList(StringPiece name) { + const google::protobuf::Field* field = BeginNamed(name, true); + if (field == NULL) return this; + + if (!ValidOneof(*field, name)) { + ++invalid_depth_; + return this; + } + + const google::protobuf::Type* type = LookupType(field); + if (type == NULL) { + ++invalid_depth_; + InvalidName(name, + StrCat("Missing descriptor for field: ", field->type_url())); + return this; + } + + element_.reset(new ProtoElement(element_.release(), field, *type, true)); + return this; +} + +ProtoWriter* ProtoWriter::EndList() { + if (invalid_depth_ > 0) { + --invalid_depth_; + } else if (element_ != NULL) { + element_.reset(element_->pop()); + } + return this; +} + +ProtoWriter* ProtoWriter::RenderDataPiece(StringPiece name, + const DataPiece& data) { + Status status; + if (invalid_depth_ > 0) return this; + + const google::protobuf::Field* field = Lookup(name); + if (field == NULL) return this; + + if (!ValidOneof(*field, name)) return this; + + const google::protobuf::Type* type = LookupType(field); + if (type == NULL) { + InvalidName(name, + StrCat("Missing descriptor for field: ", field->type_url())); + 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; +} + +bool ProtoWriter::ValidOneof(const google::protobuf::Field& field, + StringPiece unnormalized_name) { + if (element_ == NULL) return true; + + if (field.oneof_index() > 0) { + if (element_->IsOneofIndexTaken(field.oneof_index())) { + InvalidValue( + "oneof", + StrCat("oneof field '", + element_->type().oneofs(field.oneof_index() - 1), + "' is already set. Cannot set '", unnormalized_name, "'")); + return false; + } + element_->TakeOneofIndex(field.oneof_index()); + } + return true; +} + +bool ProtoWriter::IsRepeated(const google::protobuf::Field& field) { + return field.cardinality() == + google::protobuf::Field_Cardinality_CARDINALITY_REPEATED; +} + +const google::protobuf::Field* ProtoWriter::BeginNamed(StringPiece name, + bool is_list) { + if (invalid_depth_ > 0) { + ++invalid_depth_; + return NULL; + } + const google::protobuf::Field* field = Lookup(name); + if (field == NULL) { + ++invalid_depth_; + // InvalidName() already called in Lookup(). + return NULL; + } + if (is_list && !IsRepeated(*field)) { + ++invalid_depth_; + InvalidName(name, "Proto field is not repeating, cannot start list."); + return NULL; + } + return field; +} + +const google::protobuf::Field* ProtoWriter::Lookup( + StringPiece unnormalized_name) { + ProtoElement* e = element(); + if (e == NULL) { + InvalidName(unnormalized_name, "Root element must be a message."); + return NULL; + } + if (unnormalized_name.empty()) { + // Objects in repeated field inherit the same field descriptor. + if (e->parent_field() == NULL) { + InvalidName(unnormalized_name, "Proto fields must have a name."); + } else if (!IsRepeated(*e->parent_field())) { + InvalidName(unnormalized_name, "Proto fields must have a name."); + return NULL; + } + return e->parent_field(); + } + const google::protobuf::Field* field = + typeinfo_->FindField(&e->type(), unnormalized_name); + if (field == NULL) InvalidName(unnormalized_name, "Cannot find field."); + return field; +} + +const google::protobuf::Type* ProtoWriter::LookupType( + const google::protobuf::Field* field) { + return ((field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE || + field->kind() == google::protobuf::Field_Kind_TYPE_GROUP) + ? typeinfo_->GetTypeByTypeUrl(field->type_url()) + : &element_->type()); +} + +void ProtoWriter::WriteRootMessage() { + GOOGLE_DCHECK(!done_); + int curr_pos = 0; + // Calls the destructor of CodedOutputStream to remove any uninitialized + // memory from the Cord before we read it. + stream_.reset(NULL); + const void* data; + int length; + google::protobuf::io::ArrayInputStream input_stream(buffer_.data(), buffer_.size()); + while (input_stream.Next(&data, &length)) { + if (length == 0) continue; + int num_bytes = length; + // Write up to where we need to insert the size field. + // The number of bytes we may write is the smaller of: + // - the current fragment size + // - the distance to the next position where a size field needs to be + // inserted. + if (!size_insert_.empty() && + size_insert_.front().pos - curr_pos < num_bytes) { + num_bytes = size_insert_.front().pos - curr_pos; + } + output_->Append(static_cast<const char*>(data), num_bytes); + if (num_bytes < length) { + input_stream.BackUp(length - num_bytes); + } + curr_pos += num_bytes; + // Insert the size field. + // size_insert_.front(): the next <index, size> pair to be written. + // size_insert_.front().pos: position of the size field. + // size_insert_.front().size: the size (integer) to be inserted. + if (!size_insert_.empty() && curr_pos == size_insert_.front().pos) { + // Varint32 occupies at most 10 bytes. + uint8 insert_buffer[10]; + uint8* insert_buffer_pos = CodedOutputStream::WriteVarint32ToArray( + size_insert_.front().size, insert_buffer); + output_->Append(reinterpret_cast<const char*>(insert_buffer), + insert_buffer_pos - insert_buffer); + size_insert_.pop_front(); + } + } + output_->Flush(); + stream_.reset(new CodedOutputStream(&adapter_)); + done_ = true; +} + +void ProtoWriter::WriteTag(const google::protobuf::Field& field) { + WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType( + static_cast<WireFormatLite::FieldType>(field.kind())); + stream_->WriteTag(WireFormatLite::MakeTag(field.number(), wire_type)); +} + + +} // namespace converter +} // namespace util +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/util/internal/proto_writer.h b/src/google/protobuf/util/internal/proto_writer.h new file mode 100644 index 00000000..e631e56f --- /dev/null +++ b/src/google/protobuf/util/internal/proto_writer.h @@ -0,0 +1,315 @@ +// 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_UTIL_CONVERTER_PROTO_WRITER_H__ +#define GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTO_WRITER_H__ + +#include <deque> +#include <google/protobuf/stubs/hash.h> +#include <string> + +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/io/zero_copy_stream_impl.h> +#include <google/protobuf/descriptor.h> +#include <google/protobuf/util/internal/type_info.h> +#include <google/protobuf/util/internal/datapiece.h> +#include <google/protobuf/util/internal/error_listener.h> +#include <google/protobuf/util/internal/structured_objectwriter.h> +#include <google/protobuf/util/type_resolver.h> +#include <google/protobuf/stubs/bytestream.h> + +namespace google { +namespace protobuf { +namespace io { +class CodedOutputStream; +} // namespace io +} // namespace protobuf + + +namespace protobuf { +class Type; +class Field; +} // namespace protobuf + + +namespace protobuf { +namespace util { +namespace converter { + +class ObjectLocationTracker; + +// An ObjectWriter that can write protobuf bytes directly from writer events. +// This class does not support special types like Struct or Map. However, since +// this class supports raw protobuf, it can be used to provide support for +// special types by inheriting from it or by wrapping it. +// +// It also supports streaming. +class LIBPROTOBUF_EXPORT ProtoWriter : public StructuredObjectWriter { + public: +// Constructor. Does not take ownership of any parameter passed in. + ProtoWriter(TypeResolver* type_resolver, const google::protobuf::Type& type, + strings::ByteSink* output, ErrorListener* listener); + virtual ~ProtoWriter(); + + // ObjectWriter methods. + virtual ProtoWriter* StartObject(StringPiece name); + virtual ProtoWriter* EndObject(); + virtual ProtoWriter* StartList(StringPiece name); + virtual ProtoWriter* EndList(); + virtual ProtoWriter* RenderBool(StringPiece name, bool value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoWriter* RenderInt32(StringPiece name, int32 value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoWriter* RenderUint32(StringPiece name, uint32 value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoWriter* RenderInt64(StringPiece name, int64 value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoWriter* RenderUint64(StringPiece name, uint64 value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoWriter* RenderDouble(StringPiece name, double value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoWriter* RenderFloat(StringPiece name, float value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoWriter* RenderString(StringPiece name, StringPiece value) { + return RenderDataPiece(name, DataPiece(value)); + } + virtual ProtoWriter* RenderBytes(StringPiece name, StringPiece value) { + return RenderDataPiece(name, DataPiece(value, false)); + } + virtual ProtoWriter* RenderNull(StringPiece name) { + return RenderDataPiece(name, DataPiece::NullData()); + } + + // Renders a DataPiece 'value' into a field whose wire type is determined + // from the given field 'name'. + virtual ProtoWriter* RenderDataPiece(StringPiece name, + const DataPiece& value); + + // Returns the location tracker to use for tracking locations for errors. + const LocationTrackerInterface& location() { + return element_ != NULL ? *element_ : *tracker_; + } + + // When true, we finished writing to output a complete message. + bool done() const { return done_; } + + // Returns the proto stream object. + google::protobuf::io::CodedOutputStream* stream() { return stream_.get(); } + + // Getters and mutators of invalid_depth_. + void IncrementInvalidDepth() { ++invalid_depth_; } + void DecrementInvalidDepth() { --invalid_depth_; } + int invalid_depth() { return invalid_depth_; } + + ErrorListener* listener() { return listener_; } + + const TypeInfo* typeinfo() { return typeinfo_; } + + protected: + class LIBPROTOBUF_EXPORT ProtoElement : public BaseElement, public LocationTrackerInterface { + public: + // Constructor for the root element. No parent nor field. + ProtoElement(const TypeInfo* typeinfo, const google::protobuf::Type& type, + ProtoWriter* enclosing); + + // Constructor for a field of an element. + ProtoElement(ProtoElement* parent, const google::protobuf::Field* field, + const google::protobuf::Type& type, bool is_list); + + virtual ~ProtoElement() {} + + // Called just before the destructor for clean up: + // - reports any missing required fields + // - computes the space needed by the size field, and augment the + // length of all parent messages by this additional space. + // - releases and returns the parent pointer. + ProtoElement* pop(); + + // Accessors + // parent_field() may be NULL if we are at root. + const google::protobuf::Field* parent_field() const { + return parent_field_; + } + const google::protobuf::Type& type() const { return type_; } + + // Registers field for accounting required fields. + void RegisterField(const google::protobuf::Field* field); + + // To report location on error messages. + virtual string ToString() const; + + virtual ProtoElement* parent() const { + return static_cast<ProtoElement*>(BaseElement::parent()); + } + + // Returns true if the index is already taken by a preceeding oneof input. + bool IsOneofIndexTaken(int32 index); + + // Marks the oneof 'index' as taken. Future inputs to this oneof will + // generate an error. + void TakeOneofIndex(int32 index); + + private: + // Used for access to variables of the enclosing instance of + // ProtoWriter. + ProtoWriter* ow_; + + // Describes the element as a field in the parent message. + // parent_field_ is NULL if and only if this element is the root element. + const google::protobuf::Field* parent_field_; + + // TypeInfo to lookup types. + const TypeInfo* typeinfo_; + + // Additional variables if this element is a message: + // (Root element is always a message). + // type_ : the type of this element. + // required_fields_ : set of required fields. + // size_index_ : index into ProtoWriter::size_insert_ + // for later insertion of serialized message length. + const google::protobuf::Type& type_; + std::set<const google::protobuf::Field*> required_fields_; + const int size_index_; + + // Tracks position in repeated fields, needed for LocationTrackerInterface. + int array_index_; + + // Set of oneof indices already seen for the type_. Used to validate + // incoming messages so no more than one oneof is set. + hash_set<int32> oneof_indices_; + + GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoElement); + }; + + // Container for inserting 'size' information at the 'pos' position. + struct SizeInfo { + const int pos; + int size; + }; + + ProtoWriter(const TypeInfo* typeinfo, const google::protobuf::Type& type, + strings::ByteSink* output, ErrorListener* listener); + + virtual ProtoElement* element() { return element_.get(); } + + // Helper methods for calling ErrorListener. See error_listener.h. + void InvalidName(StringPiece unknown_name, StringPiece message); + void InvalidValue(StringPiece type_name, StringPiece value); + void MissingField(StringPiece missing_name); + + // Common code for BeginObject() and BeginList() that does invalid_depth_ + // bookkeeping associated with name lookup. + const google::protobuf::Field* BeginNamed(StringPiece name, bool is_list); + + // Lookup the field in the current element. Looks in the base descriptor + // and in any extension. This will report an error if the field cannot be + // found or if multiple matching extensions are found. + const google::protobuf::Field* Lookup(StringPiece name); + + // Lookup the field type in the type descriptor. Returns NULL if the type + // is not known. + const google::protobuf::Type* LookupType( + const google::protobuf::Field* field); + + // Write serialized output to the final output ByteSink, inserting all + // the size information for nested messages that are missing from the + // intermediate Cord buffer. + void WriteRootMessage(); + + // Helper method to write proto tags based on the given field. + void WriteTag(const google::protobuf::Field& field); + + + // Returns true if the field for type_ can be set as a oneof. If field is not + // a oneof type, this function does nothing and returns true. + // If another field for this oneof is already set, this function returns + // false. It also calls the appropriate error callback. + // unnormalized_name is used for error string. + bool ValidOneof(const google::protobuf::Field& field, + StringPiece unnormalized_name); + + // Returns true if the field is repeated. + bool IsRepeated(const google::protobuf::Field& field); + + private: + // Variables for describing the structure of the input tree: + // master_type_: descriptor for the whole protobuf message. + // typeinfo_ : the TypeInfo object to lookup types. + const google::protobuf::Type& master_type_; + const TypeInfo* typeinfo_; + // Whether we own the typeinfo_ object. + bool own_typeinfo_; + + // Indicates whether we finished writing root message completely. + bool done_; + + // Variable for internal state processing: + // element_ : the current element. + // size_insert_: sizes of nested messages. + // pos - position to insert the size field. + // size - size value to be inserted. + google::protobuf::scoped_ptr<ProtoElement> element_; + std::deque<SizeInfo> size_insert_; + + // Variables for output generation: + // output_ : pointer to an external ByteSink for final user-visible output. + // buffer_ : buffer holding partial message before being ready for output_. + // adapter_ : internal adapter between CodedOutputStream and buffer_. + // stream_ : wrapper for writing tags and other encodings in wire format. + strings::ByteSink* output_; + string buffer_; + google::protobuf::io::StringOutputStream adapter_; + google::protobuf::scoped_ptr<google::protobuf::io::CodedOutputStream> stream_; + + // Variables for error tracking and reporting: + // listener_ : a place to report any errors found. + // invalid_depth_: number of enclosing invalid nested messages. + // tracker_ : the root location tracker interface. + ErrorListener* listener_; + int invalid_depth_; + google::protobuf::scoped_ptr<LocationTrackerInterface> tracker_; + + GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoWriter); +}; + +} // namespace converter +} // namespace util +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTO_WRITER_H__ diff --git a/src/google/protobuf/util/internal/protostream_objectsource.cc b/src/google/protobuf/util/internal/protostream_objectsource.cc index 2bbb1597..034d616f 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource.cc +++ b/src/google/protobuf/util/internal/protostream_objectsource.cc @@ -140,10 +140,10 @@ Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type, bool include_start_and_end, ObjectWriter* ow) const { - const TypeRenderer* type_renderer = FindTypeRenderer(type.name()); - if (type_renderer != NULL) { - return (*type_renderer)(this, type, name, ow); - } + const TypeRenderer* type_renderer = FindTypeRenderer(type.name()); + if (type_renderer != NULL) { + return (*type_renderer)(this, type, name, ow); + } const google::protobuf::Field* field = NULL; string field_name; @@ -171,7 +171,9 @@ Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type, if (field->cardinality() == google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) { - if (IsMap(*field)) { + bool check_maps = true; + + if (check_maps && IsMap(*field)) { ow->StartObject(field_name); ASSIGN_OR_RETURN(tag, RenderMap(field, field_name, tag, ow)); ow->EndObject(); @@ -218,48 +220,32 @@ StatusOr<uint32> ProtoStreamObjectSource::RenderMap( const google::protobuf::Type* field_type = typeinfo_->GetTypeByTypeUrl(field->type_url()); uint32 tag_to_return = 0; - if (IsPackable(*field) && - list_tag == - WireFormatLite::MakeTag(field->number(), - WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) { - RETURN_IF_ERROR(RenderPackedMapEntry(field_type, ow)); - tag_to_return = stream_->ReadTag(); - } else { - do { - RETURN_IF_ERROR(RenderMapEntry(field_type, ow)); - } while ((tag_to_return = stream_->ReadTag()) == list_tag); - } - return tag_to_return; -} - -Status ProtoStreamObjectSource::RenderMapEntry( - const google::protobuf::Type* type, ObjectWriter* ow) const { - uint32 buffer32; - stream_->ReadVarint32(&buffer32); // message length - int old_limit = stream_->PushLimit(buffer32); - string map_key; - for (uint32 tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) { - const google::protobuf::Field* field = FindAndVerifyField(*type, tag); - if (field == NULL) { - WireFormat::SkipField(stream_, tag, NULL); - continue; - } - // Map field numbers are key = 1 and value = 2 - if (field->number() == 1) { - map_key = ReadFieldValueAsString(*field); - } else if (field->number() == 2) { - if (map_key.empty()) { - return Status(util::error::INTERNAL, "Map key must be non-empty"); + do { + // Render map entry message type. + uint32 buffer32; + stream_->ReadVarint32(&buffer32); // message length + int old_limit = stream_->PushLimit(buffer32); + string map_key; + for (uint32 tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) { + const google::protobuf::Field* field = + FindAndVerifyField(*field_type, tag); + if (field == NULL) { + WireFormat::SkipField(stream_, tag, NULL); + continue; + } + // Map field numbers are key = 1 and value = 2 + if (field->number() == 1) { + map_key = ReadFieldValueAsString(*field); + } else if (field->number() == 2) { + if (map_key.empty()) { + return Status(util::error::INTERNAL, "Map key must be non-empty"); + } + RETURN_IF_ERROR(RenderField(field, map_key, ow)); } - // Disable case normalization for map keys as they are just data. We - // retain them intact. - ow->DisableCaseNormalizationForNextKey(); - RETURN_IF_ERROR(RenderField(field, map_key, ow)); } - } - stream_->PopLimit(old_limit); - - return Status::OK; + stream_->PopLimit(old_limit); + } while ((tag_to_return = stream_->ReadTag()) == list_tag); + return tag_to_return; } Status ProtoStreamObjectSource::RenderPacked( @@ -274,18 +260,6 @@ Status ProtoStreamObjectSource::RenderPacked( return Status::OK; } -Status ProtoStreamObjectSource::RenderPackedMapEntry( - const google::protobuf::Type* type, ObjectWriter* ow) const { - uint32 length; - stream_->ReadVarint32(&length); - int old_limit = stream_->PushLimit(length); - while (stream_->BytesUntilLimit() > 0) { - RETURN_IF_ERROR(RenderMapEntry(type, ow)); - } - stream_->PopLimit(old_limit); - return Status::OK; -} - Status ProtoStreamObjectSource::RenderTimestamp( const ProtoStreamObjectSource* os, const google::protobuf::Type& type, StringPiece field_name, ObjectWriter* ow) { @@ -601,10 +575,10 @@ Status ProtoStreamObjectSource::RenderAny(const ProtoStreamObjectSource* os, // nested_type cannot be null at this time. const google::protobuf::Type* nested_type = resolved_type.ValueOrDie(); - // We know the type so we can render it. Recursively parse the nested stream - // using a nested ProtoStreamObjectSource using our nested type information. google::protobuf::io::ArrayInputStream zero_copy_stream(value.data(), value.size()); google::protobuf::io::CodedInputStream in_stream(&zero_copy_stream); + // We know the type so we can render it. Recursively parse the nested stream + // using a nested ProtoStreamObjectSource using our nested type information. ProtoStreamObjectSource nested_os(&in_stream, os->typeinfo_, *nested_type); // We manually call start and end object here so we can inject the @type. @@ -676,8 +650,7 @@ void ProtoStreamObjectSource::InitRendererMap() { &ProtoStreamObjectSource::RenderString; (*renderers_)["google.protobuf.BytesValue"] = &ProtoStreamObjectSource::RenderBytes; - (*renderers_)["google.protobuf.Any"] = - &ProtoStreamObjectSource::RenderAny; + (*renderers_)["google.protobuf.Any"] = &ProtoStreamObjectSource::RenderAny; (*renderers_)["google.protobuf.Struct"] = &ProtoStreamObjectSource::RenderStruct; (*renderers_)["google.protobuf.Value"] = @@ -704,87 +677,118 @@ ProtoStreamObjectSource::FindTypeRenderer(const string& type_url) { Status ProtoStreamObjectSource::RenderField( const google::protobuf::Field* field, StringPiece field_name, ObjectWriter* ow) const { + // Short-circuit message types as it tends to call WriteMessage recursively + // and ends up using a lot of stack space. Keep the stack usage of this + // message small in order to preserve stack space and not crash. + if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) { + uint32 buffer32; + stream_->ReadVarint32(&buffer32); // message length + int old_limit = stream_->PushLimit(buffer32); + // Get the nested message type for this field. + const google::protobuf::Type* type = + typeinfo_->GetTypeByTypeUrl(field->type_url()); + if (type == NULL) { + return Status(util::error::INTERNAL, + StrCat("Invalid configuration. Could not find the type: ", + field->type_url())); + } + + // Short-circuit any special type rendering to save call-stack space. + const TypeRenderer* type_renderer = FindTypeRenderer(type->name()); + + bool use_type_renderer = type_renderer != NULL; + + if (use_type_renderer) { + RETURN_IF_ERROR((*type_renderer)(this, *type, field_name, ow)); + } else { + RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow)); + } + if (!stream_->ConsumedEntireMessage()) { + return Status(util::error::INVALID_ARGUMENT, + "Nested protocol message not parsed in its entirety."); + } + stream_->PopLimit(old_limit); + } else { + // Render all other non-message types. + return RenderNonMessageField(field, field_name, ow); + } + return Status::OK; +} + +Status ProtoStreamObjectSource::RenderNonMessageField( + const google::protobuf::Field* field, StringPiece field_name, + ObjectWriter* ow) const { + // Temporary buffers of different types. + uint32 buffer32; + uint64 buffer64; + string strbuffer; switch (field->kind()) { case google::protobuf::Field_Kind_TYPE_BOOL: { - uint64 buffer64; stream_->ReadVarint64(&buffer64); ow->RenderBool(field_name, buffer64 != 0); break; } case google::protobuf::Field_Kind_TYPE_INT32: { - uint32 buffer32; stream_->ReadVarint32(&buffer32); ow->RenderInt32(field_name, bit_cast<int32>(buffer32)); break; } case google::protobuf::Field_Kind_TYPE_INT64: { - uint64 buffer64; stream_->ReadVarint64(&buffer64); ow->RenderInt64(field_name, bit_cast<int64>(buffer64)); break; } case google::protobuf::Field_Kind_TYPE_UINT32: { - uint32 buffer32; stream_->ReadVarint32(&buffer32); ow->RenderUint32(field_name, bit_cast<uint32>(buffer32)); break; } case google::protobuf::Field_Kind_TYPE_UINT64: { - uint64 buffer64; stream_->ReadVarint64(&buffer64); ow->RenderUint64(field_name, bit_cast<uint64>(buffer64)); break; } case google::protobuf::Field_Kind_TYPE_SINT32: { - uint32 buffer32; stream_->ReadVarint32(&buffer32); ow->RenderInt32(field_name, WireFormatLite::ZigZagDecode32(buffer32)); break; } case google::protobuf::Field_Kind_TYPE_SINT64: { - uint64 buffer64; stream_->ReadVarint64(&buffer64); ow->RenderInt64(field_name, WireFormatLite::ZigZagDecode64(buffer64)); break; } case google::protobuf::Field_Kind_TYPE_SFIXED32: { - uint32 buffer32; stream_->ReadLittleEndian32(&buffer32); ow->RenderInt32(field_name, bit_cast<int32>(buffer32)); break; } case google::protobuf::Field_Kind_TYPE_SFIXED64: { - uint64 buffer64; stream_->ReadLittleEndian64(&buffer64); ow->RenderInt64(field_name, bit_cast<int64>(buffer64)); break; } case google::protobuf::Field_Kind_TYPE_FIXED32: { - uint32 buffer32; stream_->ReadLittleEndian32(&buffer32); ow->RenderUint32(field_name, bit_cast<uint32>(buffer32)); break; } case google::protobuf::Field_Kind_TYPE_FIXED64: { - uint64 buffer64; stream_->ReadLittleEndian64(&buffer64); ow->RenderUint64(field_name, bit_cast<uint64>(buffer64)); break; } case google::protobuf::Field_Kind_TYPE_FLOAT: { - uint32 buffer32; stream_->ReadLittleEndian32(&buffer32); ow->RenderFloat(field_name, bit_cast<float>(buffer32)); break; } case google::protobuf::Field_Kind_TYPE_DOUBLE: { - uint64 buffer64; stream_->ReadLittleEndian64(&buffer64); ow->RenderDouble(field_name, bit_cast<double>(buffer64)); break; } case google::protobuf::Field_Kind_TYPE_ENUM: { - uint32 buffer32; stream_->ReadVarint32(&buffer32); // If the field represents an explicit NULL value, render null. @@ -811,40 +815,15 @@ Status ProtoStreamObjectSource::RenderField( break; } case google::protobuf::Field_Kind_TYPE_STRING: { - uint32 buffer32; - string str; stream_->ReadVarint32(&buffer32); // string size. - stream_->ReadString(&str, buffer32); - ow->RenderString(field_name, str); + stream_->ReadString(&strbuffer, buffer32); + ow->RenderString(field_name, strbuffer); break; } case google::protobuf::Field_Kind_TYPE_BYTES: { - uint32 buffer32; stream_->ReadVarint32(&buffer32); // bytes size. - string value; - stream_->ReadString(&value, buffer32); - ow->RenderBytes(field_name, value); - break; - } - case google::protobuf::Field_Kind_TYPE_MESSAGE: { - uint32 buffer32; - stream_->ReadVarint32(&buffer32); // message length - int old_limit = stream_->PushLimit(buffer32); - // Get the nested message type for this field. - const google::protobuf::Type* type = - typeinfo_->GetTypeByTypeUrl(field->type_url()); - if (type == NULL) { - return Status(util::error::INTERNAL, - StrCat("Invalid configuration. Could not find the type: ", - field->type_url())); - } - - RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow)); - if (!stream_->ConsumedEntireMessage()) { - return Status(util::error::INVALID_ARGUMENT, - "Nested protocol message not parsed in its entirety."); - } - stream_->PopLimit(old_limit); + stream_->ReadString(&strbuffer, buffer32); + ow->RenderBytes(field_name, strbuffer); break; } default: diff --git a/src/google/protobuf/util/internal/protostream_objectsource.h b/src/google/protobuf/util/internal/protostream_objectsource.h index 3cd37aa1..78defa1d 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource.h +++ b/src/google/protobuf/util/internal/protostream_objectsource.h @@ -122,20 +122,12 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { StringPiece name, uint32 list_tag, ObjectWriter* ow) const; - // Renders an entry in a map, advancing stream pointers appropriately. - util::Status RenderMapEntry(const google::protobuf::Type* type, - ObjectWriter* ow) const; - // Renders a packed repeating field. A packed field is stored as: // {tag length item1 item2 item3} instead of the less efficient // {tag item1 tag item2 tag item3}. util::Status RenderPacked(const google::protobuf::Field* field, ObjectWriter* ow) const; - // Equivalent of RenderPacked, but for map entries. - util::Status RenderPackedMapEntry(const google::protobuf::Type* type, - ObjectWriter* ow) const; - // Renders a google.protobuf.Timestamp value to ObjectWriter static util::Status RenderTimestamp(const ProtoStreamObjectSource* os, const google::protobuf::Type& type, @@ -210,6 +202,12 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { util::Status RenderField(const google::protobuf::Field* field, StringPiece field_name, ObjectWriter* ow) const; + // Same as above but renders all non-message field types. Callers don't call + // this function directly. They just use RenderField. + util::Status RenderNonMessageField(const google::protobuf::Field* field, + StringPiece field_name, + ObjectWriter* ow) const; + // Reads field value according to Field spec in 'field' and returns the read // value as string. This only works for primitive datatypes (no message @@ -238,6 +236,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { // google::protobuf::Type of the message source. const google::protobuf::Type& type_; + 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 f6e5ee7a..561f6763 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource_test.cc +++ b/src/google/protobuf/util/internal/protostream_objectsource_test.cc @@ -327,9 +327,16 @@ TEST_P(ProtostreamObjectSourceTest, RepeatingPrimitives) { DoTest(primitive, Primitive::descriptor()); } +TEST_P(ProtostreamObjectSourceTest, CustomJsonName) { + Author author; + author.set_id(12345); + + ow_.StartObject("")->RenderUint64("@id", 12345)->EndObject(); + DoTest(author, Author::descriptor()); +} + TEST_P(ProtostreamObjectSourceTest, NestedMessage) { Author* author = new Author(); - author->set_id(101L); author->set_name("Tolstoy"); Book book; book.set_title("My Book"); @@ -338,7 +345,6 @@ TEST_P(ProtostreamObjectSourceTest, NestedMessage) { ow_.StartObject("") ->RenderString("title", "My Book") ->StartObject("author") - ->RenderUint64("id", bit_cast<uint64>(101LL)) ->RenderString("name", "Tolstoy") ->EndObject() ->EndObject(); diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.cc b/src/google/protobuf/util/internal/protostream_objectwriter.cc index 0958997c..786bf0be 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter.cc +++ b/src/google/protobuf/util/internal/protostream_objectwriter.cc @@ -51,7 +51,6 @@ namespace util { namespace converter { using google::protobuf::internal::WireFormatLite; -using google::protobuf::io::CodedOutputStream; using util::error::INVALID_ARGUMENT; using util::Status; using util::StatusOr; @@ -60,230 +59,31 @@ using util::StatusOr; ProtoStreamObjectWriter::ProtoStreamObjectWriter( TypeResolver* type_resolver, const google::protobuf::Type& type, strings::ByteSink* output, ErrorListener* listener) - : master_type_(type), - typeinfo_(TypeInfo::NewTypeInfo(type_resolver)), - own_typeinfo_(true), - done_(false), - element_(NULL), - size_insert_(), - output_(output), - buffer_(), - adapter_(&buffer_), - stream_(new CodedOutputStream(&adapter_)), - listener_(listener), - invalid_depth_(0), - tracker_(new ObjectLocationTracker()) {} + : ProtoWriter(type_resolver, type, output, listener), + master_type_(type), + current_(NULL) {} ProtoStreamObjectWriter::ProtoStreamObjectWriter( const TypeInfo* typeinfo, const google::protobuf::Type& type, strings::ByteSink* output, ErrorListener* listener) - : master_type_(type), - typeinfo_(typeinfo), - own_typeinfo_(false), - done_(false), - element_(NULL), - size_insert_(), - output_(output), - buffer_(), - adapter_(&buffer_), - stream_(new CodedOutputStream(&adapter_)), - listener_(listener), - invalid_depth_(0), - tracker_(new ObjectLocationTracker()) {} + : ProtoWriter(typeinfo, type, output, listener), + master_type_(type), + current_(NULL) {} ProtoStreamObjectWriter::~ProtoStreamObjectWriter() { - if (own_typeinfo_) { - delete typeinfo_; - } - if (element_ == NULL) return; + if (current_ == NULL) return; // Cleanup explicitly in order to avoid destructor stack overflow when input // is deeply nested. // Cast to BaseElement to avoid doing additional checks (like missing fields) // during pop(). google::protobuf::scoped_ptr<BaseElement> element( - static_cast<BaseElement*>(element_.get())->pop<BaseElement>()); + static_cast<BaseElement*>(current_.get())->pop<BaseElement>()); while (element != NULL) { element.reset(element->pop<BaseElement>()); } } namespace { - -// Writes an INT32 field, including tag to the stream. -inline Status WriteInt32(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<int32> i32 = data.ToInt32(); - if (i32.ok()) { - WireFormatLite::WriteInt32(field_number, i32.ValueOrDie(), stream); - } - return i32.status(); -} - -// writes an SFIXED32 field, including tag, to the stream. -inline Status WriteSFixed32(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<int32> i32 = data.ToInt32(); - if (i32.ok()) { - WireFormatLite::WriteSFixed32(field_number, i32.ValueOrDie(), stream); - } - return i32.status(); -} - -// Writes an SINT32 field, including tag, to the stream. -inline Status WriteSInt32(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<int32> i32 = data.ToInt32(); - if (i32.ok()) { - WireFormatLite::WriteSInt32(field_number, i32.ValueOrDie(), stream); - } - return i32.status(); -} - -// Writes a FIXED32 field, including tag, to the stream. -inline Status WriteFixed32(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<uint32> u32 = data.ToUint32(); - if (u32.ok()) { - WireFormatLite::WriteFixed32(field_number, u32.ValueOrDie(), stream); - } - return u32.status(); -} - -// Writes a UINT32 field, including tag, to the stream. -inline Status WriteUInt32(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<uint32> u32 = data.ToUint32(); - if (u32.ok()) { - WireFormatLite::WriteUInt32(field_number, u32.ValueOrDie(), stream); - } - return u32.status(); -} - -// Writes an INT64 field, including tag, to the stream. -inline Status WriteInt64(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<int64> i64 = data.ToInt64(); - if (i64.ok()) { - WireFormatLite::WriteInt64(field_number, i64.ValueOrDie(), stream); - } - return i64.status(); -} - -// Writes an SFIXED64 field, including tag, to the stream. -inline Status WriteSFixed64(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<int64> i64 = data.ToInt64(); - if (i64.ok()) { - WireFormatLite::WriteSFixed64(field_number, i64.ValueOrDie(), stream); - } - return i64.status(); -} - -// Writes an SINT64 field, including tag, to the stream. -inline Status WriteSInt64(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<int64> i64 = data.ToInt64(); - if (i64.ok()) { - WireFormatLite::WriteSInt64(field_number, i64.ValueOrDie(), stream); - } - return i64.status(); -} - -// Writes a FIXED64 field, including tag, to the stream. -inline Status WriteFixed64(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<uint64> u64 = data.ToUint64(); - if (u64.ok()) { - WireFormatLite::WriteFixed64(field_number, u64.ValueOrDie(), stream); - } - return u64.status(); -} - -// Writes a UINT64 field, including tag, to the stream. -inline Status WriteUInt64(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<uint64> u64 = data.ToUint64(); - if (u64.ok()) { - WireFormatLite::WriteUInt64(field_number, u64.ValueOrDie(), stream); - } - return u64.status(); -} - -// Writes a DOUBLE field, including tag, to the stream. -inline Status WriteDouble(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<double> d = data.ToDouble(); - if (d.ok()) { - WireFormatLite::WriteDouble(field_number, d.ValueOrDie(), stream); - } - return d.status(); -} - -// Writes a FLOAT field, including tag, to the stream. -inline Status WriteFloat(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<float> f = data.ToFloat(); - if (f.ok()) { - WireFormatLite::WriteFloat(field_number, f.ValueOrDie(), stream); - } - return f.status(); -} - -// Writes a BOOL field, including tag, to the stream. -inline Status WriteBool(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<bool> b = data.ToBool(); - if (b.ok()) { - WireFormatLite::WriteBool(field_number, b.ValueOrDie(), stream); - } - return b.status(); -} - -// Writes a BYTES field, including tag, to the stream. -inline Status WriteBytes(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<string> c = data.ToBytes(); - if (c.ok()) { - WireFormatLite::WriteBytes(field_number, c.ValueOrDie(), stream); - } - return c.status(); -} - -// Writes a STRING field, including tag, to the stream. -inline Status WriteString(int field_number, const DataPiece& data, - CodedOutputStream* stream) { - StatusOr<string> s = data.ToString(); - if (s.ok()) { - WireFormatLite::WriteString(field_number, s.ValueOrDie(), stream); - } - return s.status(); -} - -// Writes an ENUM field, including tag, to the stream. -inline Status WriteEnum(int field_number, const DataPiece& data, - const google::protobuf::Enum* enum_type, - CodedOutputStream* stream) { - StatusOr<int> e = data.ToEnum(enum_type); - if (e.ok()) { - WireFormatLite::WriteEnum(field_number, e.ValueOrDie(), stream); - } - return e.status(); -} - -// Given a google::protobuf::Type, returns the set of all required fields. -std::set<const google::protobuf::Field*> GetRequiredFields( - const google::protobuf::Type& type) { - std::set<const google::protobuf::Field*> required; - for (int i = 0; i < type.fields_size(); i++) { - const google::protobuf::Field& field = type.fields(i); - if (field.cardinality() == - google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) { - required.insert(&field); - } - } - return required; -} - // Utility method to split a string representation of Timestamp or Duration and // return the parts. void SplitSecondsAndNanos(StringPiece input, StringPiece* seconds, @@ -298,6 +98,78 @@ void SplitSecondsAndNanos(StringPiece input, StringPiece* seconds, } } +Status GetNanosFromStringPiece(StringPiece s_nanos, + const char* parse_failure_message, + const char* exceeded_limit_message, + int32* nanos) { + *nanos = 0; + + // Count the number of leading 0s and consume them. + int num_leading_zeros = 0; + while (s_nanos.Consume("0")) { + num_leading_zeros++; + } + int32 i_nanos = 0; + // 's_nanos' contains fractional seconds -- i.e. 'nanos' is equal to + // "0." + s_nanos.ToString() seconds. An int32 is used for the + // conversion to 'nanos', rather than a double, so that there is no + // loss of precision. + if (!s_nanos.empty() && !safe_strto32(s_nanos.ToString(), &i_nanos)) { + return Status(INVALID_ARGUMENT, parse_failure_message); + } + if (i_nanos > kNanosPerSecond || i_nanos < 0) { + return Status(INVALID_ARGUMENT, exceeded_limit_message); + } + // s_nanos should only have digits. No whitespace. + if (s_nanos.find_first_not_of("0123456789") != StringPiece::npos) { + return Status(INVALID_ARGUMENT, parse_failure_message); + } + + if (i_nanos > 0) { + // 'scale' is the number of digits to the right of the decimal + // point in "0." + s_nanos.ToString() + int32 scale = num_leading_zeros + s_nanos.size(); + // 'conversion' converts i_nanos into nanoseconds. + // conversion = kNanosPerSecond / static_cast<int32>(std::pow(10, scale)) + // For efficiency, we precompute the conversion factor. + int32 conversion = 0; + switch (scale) { + case 1: + conversion = 100000000; + break; + case 2: + conversion = 10000000; + break; + case 3: + conversion = 1000000; + break; + case 4: + conversion = 100000; + break; + case 5: + conversion = 10000; + break; + case 6: + conversion = 1000; + break; + case 7: + conversion = 100; + break; + case 8: + conversion = 10; + break; + case 9: + conversion = 1; + break; + default: + return Status(INVALID_ARGUMENT, exceeded_limit_message); + } + *nanos = i_nanos * conversion; + } + + return Status::OK; +} + } // namespace ProtoStreamObjectWriter::AnyWriter::AnyWriter(ProtoStreamObjectWriter* parent) @@ -421,7 +293,7 @@ void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) { } // Resolve the type url, and report an error if we failed to resolve it. StatusOr<const google::protobuf::Type*> resolved_type = - parent_->typeinfo_->ResolveTypeUrl(type_url_); + parent_->typeinfo()->ResolveTypeUrl(type_url_); if (!resolved_type.ok()) { parent_->InvalidValue("Any", resolved_type.status().error_message()); invalid_ = true; @@ -440,8 +312,8 @@ void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) { // Create our object writer and initialize it with the first StartObject // call. - ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo_, *type, &output_, - parent_->listener_)); + ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo(), *type, &output_, + parent_->listener())); ow_->StartObject(""); } @@ -453,604 +325,431 @@ void ProtoStreamObjectWriter::AnyWriter::WriteAny() { } // Render the type_url and value fields directly to the stream. // type_url has tag 1 and value has tag 2. - WireFormatLite::WriteString(1, type_url_, parent_->stream_.get()); + WireFormatLite::WriteString(1, type_url_, parent_->stream()); if (!data_.empty()) { - WireFormatLite::WriteBytes(2, data_, parent_->stream_.get()); + WireFormatLite::WriteBytes(2, data_, parent_->stream()); } } -ProtoStreamObjectWriter::ProtoElement::ProtoElement( - const TypeInfo* typeinfo, const google::protobuf::Type& type, - ProtoStreamObjectWriter* enclosing) +ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter* enclosing, + ItemType item_type, bool is_placeholder, + bool is_list) : BaseElement(NULL), ow_(enclosing), any_(), - field_(NULL), - typeinfo_(typeinfo), - type_(type), - required_fields_(GetRequiredFields(type)), - is_repeated_type_(false), - size_index_(-1), - array_index_(-1), - element_type_(GetElementType(type_)) { - if (element_type_ == ANY) { + item_type_(item_type), + is_placeholder_(is_placeholder), + is_list_(is_list) { + if (item_type_ == ANY) { any_.reset(new AnyWriter(ow_)); } } -ProtoStreamObjectWriter::ProtoElement::ProtoElement( - ProtoStreamObjectWriter::ProtoElement* parent, - const google::protobuf::Field* field, const google::protobuf::Type& type, - ElementType element_type) +ProtoStreamObjectWriter::Item::Item(ProtoStreamObjectWriter::Item* parent, + ItemType item_type, bool is_placeholder, + bool is_list) : BaseElement(parent), ow_(this->parent()->ow_), any_(), - field_(field), - typeinfo_(this->parent()->typeinfo_), - type_(type), - is_repeated_type_(element_type == ProtoElement::LIST || - element_type == ProtoElement::STRUCT_LIST || - element_type == ProtoElement::MAP || - element_type == ProtoElement::STRUCT_MAP), - size_index_(!is_repeated_type_ && - field->kind() == - google::protobuf::Field_Kind_TYPE_MESSAGE - ? ow_->size_insert_.size() - : -1), - array_index_(is_repeated_type_ ? 0 : -1), - element_type_(element_type) { - if (!is_repeated_type_) { - if (field->cardinality() == - google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) { - // Update array_index_ if it is an explicit list. - if (this->parent()->array_index_ >= 0) this->parent()->array_index_++; - } else { - this->parent()->RegisterField(field); - } - if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) { - required_fields_ = GetRequiredFields(type_); - int start_pos = ow_->stream_->ByteCount(); - // length of serialized message is the final buffer position minus - // starting buffer position, plus length adjustments for size fields - // of any nested messages. We start with -start_pos here, so we only - // need to add the final buffer position to it at the end. - SizeInfo info = {start_pos, -start_pos}; - ow_->size_insert_.push_back(info); - } - } - if (element_type == ANY) { + item_type_(item_type), + is_placeholder_(is_placeholder), + is_list_(is_list) { + if (item_type == ANY) { any_.reset(new AnyWriter(ow_)); } } -ProtoStreamObjectWriter::ProtoElement* -ProtoStreamObjectWriter::ProtoElement::pop() { - // Calls the registered error listener for any required field(s) not yet - // seen. - for (set<const google::protobuf::Field*>::iterator it = - required_fields_.begin(); - it != required_fields_.end(); ++it) { - ow_->MissingField((*it)->name()); - } - // Computes the total number of proto bytes used by a message, also adjusts - // the size of all parent messages by the length of this size field. - // If size_index_ < 0, this is not a message, so no size field is added. - if (size_index_ >= 0) { - // Add the final buffer position to compute the total length of this - // serialized message. The stored value (before this addition) already - // contains the total length of the size fields of all nested messages - // minus the initial buffer position. - ow_->size_insert_[size_index_].size += ow_->stream_->ByteCount(); - // Calculate the length required to serialize the size field of the - // message, and propagate this additional size information upward to - // all enclosing messages. - int size = ow_->size_insert_[size_index_].size; - int length = CodedOutputStream::VarintSize32(size); - for (ProtoElement* e = parent(); e != NULL; e = e->parent()) { - // Only nested messages have size field, lists do not have size field. - if (e->size_index_ >= 0) { - ow_->size_insert_[e->size_index_].size += length; - } - } - } - return BaseElement::pop<ProtoElement>(); -} - -void ProtoStreamObjectWriter::ProtoElement::RegisterField( - const google::protobuf::Field* field) { - if (!required_fields_.empty() && - field->cardinality() == - google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) { - required_fields_.erase(field); - } -} - -string ProtoStreamObjectWriter::ProtoElement::ToString() const { - if (parent() == NULL) return ""; - string loc = parent()->ToString(); - if (field_->cardinality() != - google::protobuf::Field_Cardinality_CARDINALITY_REPEATED || - parent()->field_ != field_) { - string name = field_->name(); - int i = 0; - while (i < name.size() && (ascii_isalnum(name[i]) || name[i] == '_')) ++i; - if (i > 0 && i == name.size()) { // safe field name - if (loc.empty()) { - loc = name; - } else { - StrAppend(&loc, ".", name); - } - } else { - StrAppend(&loc, "[\"", CEscape(name), "\"]"); - } - } - if (field_->cardinality() == - google::protobuf::Field_Cardinality_CARDINALITY_REPEATED && - array_index_ > 0) { - StrAppend(&loc, "[", array_index_ - 1, "]"); - } - return loc.empty() ? "." : loc; -} - -bool ProtoStreamObjectWriter::ProtoElement::OneofIndexTaken(int32 index) { - return ContainsKey(oneof_indices_, index); -} - -void ProtoStreamObjectWriter::ProtoElement::TakeOneofIndex(int32 index) { - InsertIfNotPresent(&oneof_indices_, index); -} - -bool ProtoStreamObjectWriter::ProtoElement::InsertMapKeyIfNotPresent( +bool ProtoStreamObjectWriter::Item::InsertMapKeyIfNotPresent( StringPiece map_key) { return InsertIfNotPresent(&map_keys_, map_key.ToString()); } -inline void ProtoStreamObjectWriter::InvalidName(StringPiece unknown_name, - StringPiece message) { - listener_->InvalidName(location(), ToSnakeCase(unknown_name), message); -} - -inline void ProtoStreamObjectWriter::InvalidValue(StringPiece type_name, - StringPiece value) { - listener_->InvalidValue(location(), type_name, value); -} - -inline void ProtoStreamObjectWriter::MissingField(StringPiece missing_name) { - listener_->MissingField(location(), missing_name); -} - ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject( StringPiece name) { - // Starting the root message. Create the root ProtoElement and return. - if (element_ == NULL) { - if (!name.empty()) { - InvalidName(name, "Root element should not be named."); - } - element_.reset(new ProtoElement(typeinfo_, master_type_, this)); + if (invalid_depth() > 0) { + IncrementInvalidDepth(); + return this; + } + + // Starting the root message. Create the root Item and return. + // ANY message type does not need special handling, just set the ItemType + // to ANY. + if (current_ == NULL) { + ProtoWriter::StartObject(name); + current_.reset(new Item( + this, master_type_.name() == kAnyType ? Item::ANY : Item::MESSAGE, + false, false)); // If master type is a special type that needs extra values to be written to // stream, we write those values. if (master_type_.name() == kStructType) { - StartStruct(NULL); - } else if (master_type_.name() == kStructValueType) { - // We got a StartObject call with google.protobuf.Value field. This means - // we are starting an object within google.protobuf.Value type. The only - // object within that type is a struct type. So start a struct. - const google::protobuf::Field* field = StartStructValueInStruct(NULL); - StartStruct(field); + // Struct has a map<string, Value> field called "fields". + // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto + // "fields": [ + Push("fields", Item::MAP, true, true); + return this; } - return this; - } - const google::protobuf::Field* field = NULL; - if (element_ != NULL && element_->IsAny()) { - element_->any()->StartObject(name); - return this; - } else if (element_ != NULL && - (element_->IsMap() || element_->IsStructMap())) { - if (!ValidMapKey(name)) { - ++invalid_depth_; + if (master_type_.name() == kStructValueType) { + // We got a StartObject call with google.protobuf.Value field. The only + // object within that type is a struct type. So start a struct. + // + // The struct field in Value type is named "struct_value" + // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto + // Also start the map field "fields" within the struct. + // "struct_value": { + // "fields": [ + Push("struct_value", Item::MESSAGE, true, false); + Push("fields", Item::MAP, true, true); return this; } - field = StartMapEntry(name); - if (element_->IsStructMapEntry()) { - // If the top element is a map entry, this means we are starting another - // struct within a struct. - field = StartStructValueInStruct(field); + if (master_type_.name() == kStructListValueType) { + InvalidValue(kStructListValueType, + "Cannot start root message with ListValue."); } - } else if (element_ != NULL && element_->IsStructList()) { - // If the top element is a list, then we are starting a list field within a - // struct. - field = Lookup(name); - field = StartStructValueInStruct(field); - } else { - field = BeginNamed(name, false); - } - if (field == NULL) { - return this; - } - const google::protobuf::Type* type = LookupType(field); - if (type == NULL) { - ++invalid_depth_; - InvalidName(name, - StrCat("Missing descriptor for field: ", field->type_url())); return this; } - // Check to see if this field is a oneof and that no oneof in that group has - // already been set. - if (!ValidOneof(*field, name)) { - ++invalid_depth_; + // Send all ANY events to AnyWriter. + if (current_->IsAny()) { + current_->any()->StartObject(name); return this; } - if (field->type_url() == GetFullTypeWithUrl(kStructType)) { - // Start a struct object. - StartStruct(field); - } else if (field->type_url() == GetFullTypeWithUrl(kStructValueType)) { - // We got a StartObject call with google.protobuf.Value field. This means we - // are starting an object within google.protobuf.Value type. The only object - // within that type is a struct type. So start a struct. - field = StartStructValueInStruct(field); - StartStruct(field); - } else if (field->type_url() == GetFullTypeWithUrl(kAnyType)) { - // Begin an Any. We can't do the real work till we get the @type field. - WriteTag(*field); - element_.reset( - new ProtoElement(element_.release(), field, *type, ProtoElement::ANY)); - } else if (IsMap(*field)) { - // Begin a map. - // A map is triggered by a StartObject() call if the current field has a map - // type. Map values are written to proto in a manner detailed in comments - // above StartMapEntry() function. - element_.reset( - new ProtoElement(element_.release(), field, *type, ProtoElement::MAP)); - } else { - WriteTag(*field); - element_.reset(new ProtoElement(element_.release(), field, *type, - ProtoElement::MESSAGE)); - } - return this; -} - -// Proto3 maps are represented on the wire as a message with -// "key" and a "value". -// -// For example, the syntax: -// map<key_type, value_type> map_field = N; -// -// is represented as: -// message MapFieldEntry { -// option map_entry = true; // marks the map construct in the descriptor -// -// key_type key = 1; -// value_type value = 2; -// } -// repeated MapFieldEntry map_field = N; -// -// See go/proto3-maps for more information. -const google::protobuf::Field* ProtoStreamObjectWriter::StartMapEntry( - StringPiece name) { - // top of stack is already a map field - const google::protobuf::Field* field = element_->field(); - const google::protobuf::Type& type = element_->type(); - // If we come from a regular map, use MAP_ENTRY or if we come from a struct, - // use STRUCT_MAP_ENTRY. These values are used later in StartObject/StartList - // or RenderDataPiece for making appropriate decisions. - ProtoElement::ElementType element_type = element_->IsStructMap() - ? ProtoElement::STRUCT_MAP_ENTRY - : ProtoElement::MAP_ENTRY; - WriteTag(*field); - element_.reset( - new ProtoElement(element_.release(), field, type, element_type)); - RenderDataPiece("key", DataPiece(name)); - return BeginNamed("value", false); -} + // If we are within a map, we render name as keys and send StartObject to the + // value field. + if (current_->IsMap()) { + if (!ValidMapKey(name)) { + IncrementInvalidDepth(); + return this; + } -// Starts a google.protobuf.Struct. -// 'field' represents a field in a message of type google.protobuf.Struct. A -// struct contains a map with name 'fields'. This function starts this map as -// well. -// When 'field' is NULL, it means that the top level message is of struct -// type. -void ProtoStreamObjectWriter::StartStruct( - const google::protobuf::Field* field) { - const google::protobuf::Type* type = NULL; - if (field) { - type = LookupType(field); - WriteTag(*field); - element_.reset(new ProtoElement(element_.release(), field, *type, - ProtoElement::STRUCT)); - } - const google::protobuf::Field* struct_field = BeginNamed("fields", false); + // Map is a repeated field of message type with a "key" and a "value" field. + // https://developers.google.com/protocol-buffers/docs/proto3?hl=en#maps + // message MapFieldEntry { + // key_type key = 1; + // value_type value = 2; + // } + // + // repeated MapFieldEntry map_field = N; + // + // That means, we render the following element within a list (hence no + // name): + // { "key": "<name>", "value": { + Push("", Item::MESSAGE, false, false); + ProtoWriter::RenderDataPiece("key", DataPiece(name)); + Push("value", Item::MESSAGE, true, false); + + // Make sure we are valid so far after starting map fields. + if (invalid_depth() > 0) return this; + + // If top of stack is g.p.Struct type, start the struct the map field within + // it. + if (element() != NULL && IsStruct(*element()->parent_field())) { + // Render "fields": [ + Push("fields", Item::MAP, true, true); + return this; + } - if (!struct_field) { - // It is a programmatic error if this happens. Log an error. - GOOGLE_LOG(ERROR) << "Invalid internal state. Cannot find 'fields' within " - << (field ? field->type_url() : "google.protobuf.Struct"); - return; + // If top of stack is g.p.Value type, start the Struct within it. + if (element() != NULL && IsStructValue(*element()->parent_field())) { + // Render + // "struct_value": { + // "fields": [ + Push("struct_value", Item::MESSAGE, true, false); + Push("fields", Item::MAP, true, true); + } + return this; } - type = LookupType(struct_field); - element_.reset(new ProtoElement(element_.release(), struct_field, *type, - ProtoElement::STRUCT_MAP)); -} + const google::protobuf::Field* field = BeginNamed(name, false); + if (field == NULL) return this; -// Starts a "struct_value" within struct.proto's google.protobuf.Value type. -// 'field' should be of the type google.protobuf.Value. -// Returns the field identifying "struct_value" within the given field. -// -// If field is NULL, then we are starting struct_value at the top-level, in -// this case skip writing any tag information for the passed field. -const google::protobuf::Field* -ProtoStreamObjectWriter::StartStructValueInStruct( - const google::protobuf::Field* field) { - if (field) { - const google::protobuf::Type* type = LookupType(field); - WriteTag(*field); - element_.reset(new ProtoElement(element_.release(), field, *type, - ProtoElement::STRUCT_VALUE)); - } - return BeginNamed("struct_value", false); -} - -// Starts a "list_value" within struct.proto's google.protobuf.Value type. -// 'field' should be of the type google.protobuf.Value. -// Returns the field identifying "list_value" within the given field. -// -// If field is NULL, then we are starting list_value at the top-level, in -// this case skip writing any tag information for the passed field. -const google::protobuf::Field* ProtoStreamObjectWriter::StartListValueInStruct( - const google::protobuf::Field* field) { - if (field) { - const google::protobuf::Type* type = LookupType(field); - WriteTag(*field); - element_.reset(new ProtoElement(element_.release(), field, *type, - ProtoElement::STRUCT_VALUE)); + if (IsStruct(*field)) { + // Start a struct object. + // Render + // "<name>": { + // "fields": { + Push(name, Item::MESSAGE, false, false); + Push("fields", Item::MAP, true, true); + return this; } - const google::protobuf::Field* list_value = BeginNamed("list_value", false); - if (!list_value) { - // It is a programmatic error if this happens. Log an error. - GOOGLE_LOG(ERROR) << "Invalid internal state. Cannot find 'list_value' within " - << (field ? field->type_url() : "google.protobuf.Value"); - return field; + if (IsStructValue(*field)) { + // We got a StartObject call with google.protobuf.Value field. The only + // object within that type is a struct type. So start a struct. + // Render + // "<name>": { + // "struct_value": { + // "fields": { + Push(name, Item::MESSAGE, false, false); + Push("struct_value", Item::MESSAGE, true, false); + Push("fields", Item::MAP, true, true); + return this; } - return StartRepeatedValuesInListValue(list_value); -} - -// Starts the repeated "values" field in struct.proto's -// google.protobuf.ListValue type. 'field' should be of type -// google.protobuf.ListValue. -// -// If field is NULL, then we are starting ListValue at the top-level, in -// this case skip writing any tag information for the passed field. -const google::protobuf::Field* -ProtoStreamObjectWriter::StartRepeatedValuesInListValue( - const google::protobuf::Field* field) { - if (field) { - const google::protobuf::Type* type = LookupType(field); - WriteTag(*field); - element_.reset(new ProtoElement(element_.release(), field, *type, - ProtoElement::STRUCT_LIST_VALUE)); + if (IsMap(*field)) { + // Begin a map. A map is triggered by a StartObject() call if the current + // field has a map type. + // A map type is always repeated, hence set is_list to true. + // Render + // "<name>": [ + Push(name, Item::MAP, false, true); + return this; } - return BeginNamed("values", true); -} -void ProtoStreamObjectWriter::SkipElements() { - if (element_ == NULL) return; - - ProtoElement::ElementType element_type = element_->element_type(); - while (element_type == ProtoElement::STRUCT || - element_type == ProtoElement::STRUCT_LIST_VALUE || - element_type == ProtoElement::STRUCT_VALUE || - element_type == ProtoElement::STRUCT_MAP_ENTRY || - element_type == ProtoElement::MAP_ENTRY) { - element_.reset(element_->pop()); - element_type = - element_ != NULL ? element_->element_type() : ProtoElement::MESSAGE; - } + // A regular message type. Pass it directly to ProtoWriter. + // Render + // "<name>": { + Push(name, IsAny(*field) ? Item::ANY : Item::MESSAGE, false, false); + return this; } ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndObject() { - if (invalid_depth_ > 0) { - --invalid_depth_; + if (invalid_depth() > 0) { + DecrementInvalidDepth(); return this; } - if (element_ != NULL && element_->IsAny()) { - if (element_->any()->EndObject()) { - return this; - } - } - if (element_ != NULL) { - element_.reset(element_->pop()); - } - // Skip sentinel elements added to keep track of new proto3 types - map, - // struct. - SkipElements(); + if (current_ == NULL) return this; - - // If ending the root element, - // then serialize the full message with calculated sizes. - if (element_ == NULL) { - WriteRootMessage(); + if (current_->IsAny()) { + if (current_->any()->EndObject()) return this; } + + Pop(); + return this; } ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) { - const google::protobuf::Field* field = NULL; + if (invalid_depth() > 0) { + IncrementInvalidDepth(); + return this; + } + // Since we cannot have a top-level repeated item in protobuf, the only way - // element_ can be null when here is when we start a top-level list - // google.protobuf.ListValue. - if (element_ == NULL) { + // this is valid is if we start a special type google.protobuf.ListValue or + // google.protobuf.Value. + if (current_ == NULL) { if (!name.empty()) { InvalidName(name, "Root element should not be named."); + IncrementInvalidDepth(); + return this; } - element_.reset(new ProtoElement(typeinfo_, master_type_, this)); // If master type is a special type that needs extra values to be written to // stream, we write those values. if (master_type_.name() == kStructValueType) { // We got a StartList with google.protobuf.Value master type. This means // we have to start the "list_value" within google.protobuf.Value. - field = StartListValueInStruct(NULL); - } else if (master_type_.name() == kStructListValueType) { + // + // See + // https://github.com/google/protobuf/blob/master/src/google/protobuf/struct.proto + // + // Render + // "<name>": { + // "list_value": { + // "values": [ // Start this list. + ProtoWriter::StartObject(name); + current_.reset(new Item(this, Item::MESSAGE, false, false)); + Push("list_value", Item::MESSAGE, true, false); + Push("values", Item::MESSAGE, true, true); + return this; + } + + if (master_type_.name() == kStructListValueType) { // We got a StartList with google.protobuf.ListValue master type. This // means we have to start the "values" within google.protobuf.ListValue. - field = StartRepeatedValuesInListValue(NULL); + // + // Render + // "<name>": { + // "values": [ // Start this list. + ProtoWriter::StartObject(name); + current_.reset(new Item(this, Item::MESSAGE, false, false)); + Push("values", Item::MESSAGE, true, true); + return this; } - // field is NULL when master_type_ is anything other than - // google.protobuf.Value or google.protobuf.ListValue. - if (field) { - const google::protobuf::Type* type = LookupType(field); - element_.reset(new ProtoElement(element_.release(), field, *type, - ProtoElement::STRUCT_LIST)); - } + // Send the event to ProtoWriter so proper errors can be reported. + // + // Render a regular list: + // "<name>": [ + ProtoWriter::StartList(name); + current_.reset(new Item(this, Item::MESSAGE, false, true)); return this; } - if (element_->IsAny()) { - element_->any()->StartList(name); + if (current_->IsAny()) { + current_->any()->StartList(name); return this; } - // The type of element we push to stack. - ProtoElement::ElementType element_type = ProtoElement::LIST; - // Check if we need to start a map. This can heppen when there is either a map - // or a struct type within a list. - if (element_->IsMap() || element_->IsStructMap()) { + // If the top of stack is a map, we are starting a list value within a map. + // Since map does not allow repeated values, this can only happen when the map + // value is of a special type that renders a list in JSON. These can be one + // of 3 cases: + // i. We are rendering a list value within google.protobuf.Struct + // ii. We are rendering a list value within google.protobuf.Value + // iii. We are rendering a list value with type google.protobuf.ListValue. + if (current_->IsMap()) { if (!ValidMapKey(name)) { - ++invalid_depth_; + IncrementInvalidDepth(); return this; } - field = StartMapEntry(name); - if (field == NULL) return this; + // Start the repeated map entry object. + // Render + // { "key": "<name>", "value": { + Push("", Item::MESSAGE, false, false); + ProtoWriter::RenderDataPiece("key", DataPiece(name)); + Push("value", Item::MESSAGE, true, false); + + // Make sure we are valid after pushing all above items. + if (invalid_depth() > 0) return this; + + // case i and ii above. Start "list_value" field within g.p.Value + if (element() != NULL && element()->parent_field() != NULL) { + // Render + // "list_value": { + // "values": [ // Start this list + if (IsStructValue(*element()->parent_field())) { + Push("list_value", Item::MESSAGE, true, false); + Push("values", Item::MESSAGE, true, true); + return this; + } - if (element_->IsStructMapEntry()) { - // If the top element is a map entry, this means we are starting a list - // within a struct or a map. - // An example sequence of calls would be - // StartObject -> StartList - field = StartListValueInStruct(field); - if (field == NULL) return this; + // Render + // "values": [ + if (IsStructListValue(*element()->parent_field())) { + // case iii above. Bind directly to g.p.ListValue + Push("values", Item::MESSAGE, true, true); + return this; + } } - element_type = ProtoElement::STRUCT_LIST; - } else if (element_->IsStructList()) { - // If the top element is a STRUCT_LIST, this means we are starting a list - // within the current list (inside a struct). - // An example call sequence would be - // StartObject -> StartList -> StartList - // with StartObject starting a struct. + // Report an error. + InvalidValue("Map", StrCat("Cannot have repeated items ('", name, + "') within a map.")); + return this; + } - // Lookup the last list type in element stack as we are adding an element of - // the same type. - field = Lookup(name); - if (field == NULL) return this; + // When name is empty and stack is not empty, we are rendering an item within + // a list. + if (name.empty()) { + if (element() != NULL && element()->parent_field() != NULL) { + if (IsStructValue(*element()->parent_field())) { + // Since it is g.p.Value, we bind directly to the list_value. + // Render + // { // g.p.Value item within the list + // "list_value": { + // "values": [ + Push("", Item::MESSAGE, false, false); + Push("list_value", Item::MESSAGE, true, false); + Push("values", Item::MESSAGE, true, true); + return this; + } - field = StartListValueInStruct(field); - if (field == NULL) return this; + if (IsStructListValue(*element()->parent_field())) { + // Since it is g.p.ListValue, we bind to it directly. + // Render + // { // g.p.ListValue item within the list + // "values": [ + Push("", Item::MESSAGE, false, false); + Push("values", Item::MESSAGE, true, true); + return this; + } + } - element_type = ProtoElement::STRUCT_LIST; - } else { - // Lookup field corresponding to 'name'. If it is a google.protobuf.Value - // or google.protobuf.ListValue type, then StartList is a valid call, start - // this list. - // We cannot use Lookup() here as it will produce InvalidName() error if the - // field is not found. We do not want to error here as it would cause us to - // report errors twice, once here and again later in BeginNamed() call. - // Also we ignore if the field is not found here as it is caught later. - field = typeinfo_->FindField(&element_->type(), name); - - // Only check for oneof collisions on the first StartList call. We identify - // the first call with !name.empty() check. Subsequent list element calls - // will not have the name filled. - if (!name.empty() && field && !ValidOneof(*field, name)) { - ++invalid_depth_; + // Pass the event to underlying ProtoWriter. + Push(name, Item::MESSAGE, false, true); + return this; + } + + // name is not empty + const google::protobuf::Field* field = Lookup(name); + if (field == NULL) { + IncrementInvalidDepth(); + return this; + } + + if (IsStructValue(*field)) { + // If g.p.Value is repeated, start that list. Otherwise, start the + // "list_value" within it. + if (IsRepeated(*field)) { + // Render it just like a regular repeated field. + // "<name>": [ + Push(name, Item::MESSAGE, false, true); return this; } - // It is an error to try to bind to map, which behind the scenes is a list. - if (field && IsMap(*field)) { - // Push field to stack for error location tracking & reporting. - element_.reset(new ProtoElement(element_.release(), field, - *LookupType(field), - ProtoElement::MESSAGE)); - InvalidValue("Map", "Cannot bind a list to map."); - ++invalid_depth_; - element_.reset(element_->pop()); + // Start the "list_value" field. + // Render + // "<name>": { + // "list_value": { + // "values": [ + Push(name, Item::MESSAGE, false, false); + Push("list_value", Item::MESSAGE, true, false); + Push("values", Item::MESSAGE, true, true); + return this; + } + + if (IsStructListValue(*field)) { + // If g.p.ListValue is repeated, start that list. Otherwise, start the + // "values" within it. + if (IsRepeated(*field)) { + // Render it just like a regular repeated field. + // "<name>": [ + Push(name, Item::MESSAGE, false, true); return this; } - if (field && field->type_url() == GetFullTypeWithUrl(kStructValueType)) { - // There are 2 cases possible: - // a. g.p.Value is repeated - // b. g.p.Value is not repeated - // - // For case (a), the StartList should bind to the repeated g.p.Value. - // For case (b), the StartList should bind to g.p.ListValue within the - // g.p.Value. - // - // This means, for case (a), we treat it just like any other repeated - // message, except we would apply an appropriate element_type so future - // Start or Render calls are routed appropriately. - if (field->cardinality() != - google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) { - field = StartListValueInStruct(field); - } - element_type = ProtoElement::STRUCT_LIST; - } else if (field && - field->type_url() == GetFullTypeWithUrl(kStructListValueType)) { - // We got a StartList with google.protobuf.ListValue master type. This - // means we have to start the "values" within google.protobuf.ListValue. - field = StartRepeatedValuesInListValue(field); - } else { - // If no special types are to be bound, fall back to normal processing of - // StartList. - field = BeginNamed(name, true); - } - if (field == NULL) return this; + // Start the "values" field within g.p.ListValue. + // Render + // "<name>": { + // "values": [ + Push(name, Item::MESSAGE, false, false); + Push("values", Item::MESSAGE, true, true); + return this; } - const google::protobuf::Type* type = LookupType(field); - if (type == NULL) { - ++invalid_depth_; - InvalidName(name, - StrCat("Missing descriptor for field: ", field->type_url())); + // If we are here, the field should be repeated. Report an error otherwise. + if (!IsRepeated(*field)) { + IncrementInvalidDepth(); + InvalidName(name, "Proto field is not repeating, cannot start list."); return this; } - element_.reset( - new ProtoElement(element_.release(), field, *type, element_type)); + if (IsMap(*field)) { + InvalidValue("Map", + StrCat("Cannot bind a list to map for field '", name, "'.")); + IncrementInvalidDepth(); + return this; + } + + // Pass the event to ProtoWriter. + // Render + // "<name>": [ + Push(name, Item::MESSAGE, false, true); return this; } ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndList() { - if (invalid_depth_ > 0) { - --invalid_depth_; - } else if (element_ != NULL) { - if (element_->IsAny()) { - element_->any()->EndList(); - } else { - element_.reset(element_->pop()); - // Skip sentinel elements added to keep track of new proto3 types - map, - // struct. - SkipElements(); - } + if (invalid_depth() > 0) { + DecrementInvalidDepth(); + return this; } - // When element_ is NULL, we have reached the root message type. Write out - // the bytes. - if (element_ == NULL) { - WriteRootMessage(); + if (current_ == NULL) return this; + + if (current_->IsAny()) { + current_->any()->EndList(); + return this; } + + Pop(); return this; } @@ -1083,7 +782,7 @@ Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow, "null values are supported."); } } - ow->RenderDataPiece(struct_field_name, data); + ow->ProtoWriter::RenderDataPiece(struct_field_name, data); return Status::OK; } @@ -1105,15 +804,15 @@ Status ProtoStreamObjectWriter::RenderTimestamp(ProtoStreamObjectWriter* ow, } - ow->RenderDataPiece("seconds", DataPiece(seconds)); - ow->RenderDataPiece("nanos", DataPiece(nanos)); + ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds)); + ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos)); return Status::OK; } static inline util::Status RenderOneFieldPath(ProtoStreamObjectWriter* ow, StringPiece path) { - ow->RenderDataPiece("paths", - DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase))); + ow->ProtoWriter::RenderDataPiece( + "paths", DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase))); return Status::OK; } @@ -1162,224 +861,133 @@ Status ProtoStreamObjectWriter::RenderDuration(ProtoStreamObjectWriter* ow, "Invalid duration format, failed to parse seconds"); } - double d_nanos = 0; - if (!safe_strtod("0." + s_nanos.ToString(), &d_nanos)) { - return Status(INVALID_ARGUMENT, - "Invalid duration format, failed to parse nanos seconds"); + int32 nanos = 0; + Status nanos_status = GetNanosFromStringPiece( + s_nanos, "Invalid duration format, failed to parse nano seconds", + "Duration value exceeds limits", &nanos); + if (!nanos_status.ok()) { + return nanos_status; } + nanos = sign * nanos; - int32 nanos = sign * static_cast<int32>(d_nanos * kNanosPerSecond); int64 seconds = sign * unsigned_seconds; - if (seconds > kMaxSeconds || seconds < kMinSeconds || nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) { return Status(INVALID_ARGUMENT, "Duration value exceeds limits"); } - ow->RenderDataPiece("seconds", DataPiece(seconds)); - ow->RenderDataPiece("nanos", DataPiece(nanos)); + ow->ProtoWriter::RenderDataPiece("seconds", DataPiece(seconds)); + ow->ProtoWriter::RenderDataPiece("nanos", DataPiece(nanos)); return Status::OK; } Status ProtoStreamObjectWriter::RenderWrapperType(ProtoStreamObjectWriter* ow, const DataPiece& data) { - ow->RenderDataPiece("value", data); + ow->ProtoWriter::RenderDataPiece("value", data); return Status::OK; } ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( StringPiece name, const DataPiece& data) { Status status; - if (invalid_depth_ > 0) return this; - if (element_ != NULL && element_->IsAny()) { - element_->any()->RenderDataPiece(name, data); + if (invalid_depth() > 0) return this; + + if (current_ == NULL) { + const TypeRenderer* type_renderer = + FindTypeRenderer(GetFullTypeWithUrl(master_type_.name())); + if (type_renderer == NULL) { + InvalidName(name, "Root element must be a message."); + return this; + } + // Render the special type. + // "<name>": { + // ... Render special type ... + // } + ProtoWriter::StartObject(name); + status = (*type_renderer)(this, data); + if (!status.ok()) { + InvalidValue(master_type_.name(), + StrCat("Field '", name, "', ", status.error_message())); + } + ProtoWriter::EndObject(); + return this; + } + + if (current_->IsAny()) { + current_->any()->RenderDataPiece(name, data); return this; } const google::protobuf::Field* field = NULL; - string type_url; - bool is_map_entry = false; - // We are at the root when element_ == NULL. - if (element_ == NULL) { - type_url = GetFullTypeWithUrl(master_type_.name()); - } else { - if (element_->IsMap() || element_->IsStructMap()) { - if (!ValidMapKey(name)) return this; - is_map_entry = true; - field = StartMapEntry(name); - } else { - field = Lookup(name); - } + if (current_->IsMap()) { + if (!ValidMapKey(name)) return this; + + // Render an item in repeated map list. + // { "key": "<name>", "value": + Push("", Item::MESSAGE, false, false); + ProtoWriter::RenderDataPiece("key", DataPiece(name)); + field = Lookup("value"); if (field == NULL) { + GOOGLE_LOG(DFATAL) << "Map does not have a value field."; + return this; + } + + const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url()); + if (type_renderer != NULL) { + // Map's value type is a special type. Render it like a message: + // "value": { + // ... Render special type ... + // } + Push("value", Item::MESSAGE, true, false); + status = (*type_renderer)(this, data); + if (!status.ok()) { + InvalidValue(field->type_url(), + StrCat("Field '", name, "', ", status.error_message())); + } + Pop(); return this; } - // Check to see if this field is a oneof and that no oneof in that group has - // already been set. - if (!ValidOneof(*field, name)) return this; + // If we are rendering explicit null values and the backend proto field is + // not of the google.protobuf.NullType type, we do nothing. + if (data.type() == DataPiece::TYPE_NULL && + field->type_url() != kStructNullValueTypeUrl) { + return this; + } - type_url = field->type_url(); + // Render the map value as a primitive type. + ProtoWriter::RenderDataPiece("value", data); + Pop(); + return this; } - // Check if there are any well known type renderers available for type_url. - const TypeRenderer* type_renderer = FindTypeRenderer(type_url); + field = Lookup(name); + if (field == NULL) return this; + + // Check if the field is of special type. Render it accordingly if so. + const TypeRenderer* type_renderer = FindTypeRenderer(field->type_url()); if (type_renderer != NULL) { - // Push the current element to stack so lookups in type_renderer will - // find the fields. We do an EndObject soon after, which pops this. This is - // safe because all well-known types are messages. - if (element_ == NULL) { - element_.reset(new ProtoElement(typeinfo_, master_type_, this)); - } else { - if (field) { - WriteTag(*field); - const google::protobuf::Type* type = LookupType(field); - element_.reset(new ProtoElement(element_.release(), field, *type, - ProtoElement::MESSAGE)); - } - } + Push(name, Item::MESSAGE, false, false); status = (*type_renderer)(this, data); if (!status.ok()) { - InvalidValue(type_url, + InvalidValue(field->type_url(), StrCat("Field '", name, "', ", status.error_message())); } - EndObject(); - return this; - } else if (element_ == NULL) { // no message type found at root - element_.reset(new ProtoElement(typeinfo_, master_type_, this)); - InvalidName(name, "Root element must be a message."); + Pop(); return this; } - if (field == NULL) { - return this; - } - const google::protobuf::Type* type = LookupType(field); - if (type == NULL) { - InvalidName(name, - StrCat("Missing descriptor for field: ", field->type_url())); + // If we are rendering explicit null values and the backend proto field is + // not of the google.protobuf.NullType type, we do nothing. + if (data.type() == DataPiece::TYPE_NULL && + field->type_url() != kStructNullValueTypeUrl) { return this; } - // Whether we should pop at the end. Set to true if the data field is a - // message type, which can happen in case of struct values. - bool should_pop = false; - - RenderSimpleDataPiece(*field, *type, data); - - if (should_pop && element_ != NULL) { - element_.reset(element_->pop()); - } - - if (is_map_entry) { - // Ending map is the same as ending an object. - EndObject(); - } + ProtoWriter::RenderDataPiece(name, data); return this; } -void ProtoStreamObjectWriter::RenderSimpleDataPiece( - const google::protobuf::Field& field, const google::protobuf::Type& type, - const DataPiece& data) { - // If we are rendering explicit null values and the backend proto field is not - // of the google.protobuf.NullType type, we do nothing. - if (data.type() == DataPiece::TYPE_NULL && - field.type_url() != kStructNullValueTypeUrl) { - return; - } - - // 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, - ProtoElement::MESSAGE)); - - // Make sure that field represents a simple data type. - 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("")); - return; - } - - Status status; - 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()); -} - // Map of functions that are responsible for rendering well known type // represented by the key. hash_map<string, ProtoStreamObjectWriter::TypeRenderer>* @@ -1446,45 +1054,12 @@ ProtoStreamObjectWriter::FindTypeRenderer(const string& type_url) { return FindOrNull(*renderers_, type_url); } -ProtoStreamObjectWriter::ProtoElement::ElementType -ProtoStreamObjectWriter::GetElementType(const google::protobuf::Type& type) { - if (type.name() == kAnyType) { - return ProtoElement::ANY; - } else if (type.name() == kStructType) { - return ProtoElement::STRUCT; - } else if (type.name() == kStructValueType) { - return ProtoElement::STRUCT_VALUE; - } else if (type.name() == kStructListValueType) { - return ProtoElement::STRUCT_LIST_VALUE; - } else { - return ProtoElement::MESSAGE; - } -} - -bool ProtoStreamObjectWriter::ValidOneof(const google::protobuf::Field& field, - StringPiece unnormalized_name) { - if (element_ == NULL) return true; - - if (field.oneof_index() > 0) { - if (element_->OneofIndexTaken(field.oneof_index())) { - InvalidValue( - "oneof", - StrCat("oneof field '", - element_->type().oneofs(field.oneof_index() - 1), - "' is already set. Cannot set '", unnormalized_name, "'")); - return false; - } - element_->TakeOneofIndex(field.oneof_index()); - } - return true; -} - bool ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) { - if (element_ == NULL) return true; + if (current_ == NULL) return true; - if (!element_->InsertMapKeyIfNotPresent(unnormalized_name)) { - InvalidName( - unnormalized_name, + if (!current_->InsertMapKeyIfNotPresent(unnormalized_name)) { + listener()->InvalidName( + location(), unnormalized_name, StrCat("Repeated map key: '", unnormalized_name, "' is already set.")); return false; } @@ -1492,134 +1067,30 @@ bool ProtoStreamObjectWriter::ValidMapKey(StringPiece unnormalized_name) { return true; } -const google::protobuf::Field* ProtoStreamObjectWriter::BeginNamed( - StringPiece name, bool is_list) { - if (invalid_depth_ > 0) { - ++invalid_depth_; - return NULL; - } - const google::protobuf::Field* field = Lookup(name); - if (field == NULL) { - ++invalid_depth_; - // InvalidName() already called in Lookup(). - return NULL; - } - if (is_list && - field->cardinality() != - google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) { - ++invalid_depth_; - InvalidName(name, "Proto field is not repeating, cannot start list."); - return NULL; - } - return field; -} +void ProtoStreamObjectWriter::Push(StringPiece name, Item::ItemType item_type, + bool is_placeholder, bool is_list) { + is_list ? ProtoWriter::StartList(name) : ProtoWriter::StartObject(name); -const google::protobuf::Field* ProtoStreamObjectWriter::Lookup( - StringPiece unnormalized_name) { - ProtoElement* e = element(); - if (e == NULL) { - InvalidName(unnormalized_name, "Root element must be a message."); - return NULL; - } - if (unnormalized_name.empty()) { - // Objects in repeated field inherit the same field descriptor. - if (e->field() == NULL) { - InvalidName(unnormalized_name, "Proto fields must have a name."); - } else if (e->field()->cardinality() != - google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) { - InvalidName(unnormalized_name, "Proto fields must have a name."); - return NULL; - } - return e->field(); - } - const google::protobuf::Field* field = - typeinfo_->FindField(&e->type(), unnormalized_name); - if (field == NULL) InvalidName(unnormalized_name, "Cannot find field."); - return field; -} - -const google::protobuf::Type* ProtoStreamObjectWriter::LookupType( - const google::protobuf::Field* field) { - return (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE - ? typeinfo_->GetTypeByTypeUrl(field->type_url()) - : &element_->type()); + // invalid_depth == 0 means it is a successful StartObject or StartList. + if (invalid_depth() == 0) + current_.reset( + new Item(current_.release(), item_type, is_placeholder, is_list)); } -// Looks up the oneof struct field based on the data type. -StatusOr<const google::protobuf::Field*> -ProtoStreamObjectWriter::LookupStructField(DataPiece::Type type) { - const google::protobuf::Field* field = NULL; - switch (type) { - // Our JSON parser parses numbers as either int64, uint64, or double. - case DataPiece::TYPE_INT64: - case DataPiece::TYPE_UINT64: - case DataPiece::TYPE_DOUBLE: { - field = Lookup("number_value"); - break; - } - case DataPiece::TYPE_STRING: { - field = Lookup("string_value"); - break; - } - case DataPiece::TYPE_BOOL: { - field = Lookup("bool_value"); - break; - } - case DataPiece::TYPE_NULL: { - field = Lookup("null_value"); - break; - } - default: { return Status(INVALID_ARGUMENT, "Invalid struct data type"); } +void ProtoStreamObjectWriter::Pop() { + // Pop all placeholder items sending StartObject or StartList events to + // ProtoWriter according to is_list value. + while (current_ != NULL && current_->is_placeholder()) { + PopOneElement(); } - if (field == NULL) { - return Status(INVALID_ARGUMENT, "Could not lookup struct field"); + if (current_ != NULL) { + PopOneElement(); } - return field; } -void ProtoStreamObjectWriter::WriteRootMessage() { - GOOGLE_DCHECK(!done_); - int curr_pos = 0; - // Calls the destructor of CodedOutputStream to remove any uninitialized - // memory from the Cord before we read it. - stream_.reset(NULL); - const void* data; - int length; - google::protobuf::io::ArrayInputStream input_stream(buffer_.data(), buffer_.size()); - while (input_stream.Next(&data, &length)) { - if (length == 0) continue; - int num_bytes = length; - // Write up to where we need to insert the size field. - // The number of bytes we may write is the smaller of: - // - the current fragment size - // - the distance to the next position where a size field needs to be - // inserted. - if (!size_insert_.empty() && - size_insert_.front().pos - curr_pos < num_bytes) { - num_bytes = size_insert_.front().pos - curr_pos; - } - output_->Append(static_cast<const char*>(data), num_bytes); - if (num_bytes < length) { - input_stream.BackUp(length - num_bytes); - } - curr_pos += num_bytes; - // Insert the size field. - // size_insert_.front(): the next <index, size> pair to be written. - // size_insert_.front().pos: position of the size field. - // size_insert_.front().size: the size (integer) to be inserted. - if (!size_insert_.empty() && curr_pos == size_insert_.front().pos) { - // Varint32 occupies at most 10 bytes. - uint8 insert_buffer[10]; - uint8* insert_buffer_pos = CodedOutputStream::WriteVarint32ToArray( - size_insert_.front().size, insert_buffer); - output_->Append(reinterpret_cast<const char*>(insert_buffer), - insert_buffer_pos - insert_buffer); - size_insert_.pop_front(); - } - } - output_->Flush(); - stream_.reset(new CodedOutputStream(&adapter_)); - done_ = true; +void ProtoStreamObjectWriter::PopOneElement() { + current_->is_list() ? ProtoWriter::EndList() : ProtoWriter::EndObject(); + current_.reset(current_->pop<Item>()); } bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) { @@ -1630,7 +1101,7 @@ bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) { return false; } const google::protobuf::Type* field_type = - typeinfo_->GetTypeByTypeUrl(field.type_url()); + typeinfo()->GetTypeByTypeUrl(field.type_url()); // TODO(xiaofeng): Unify option names. return GetBoolOptionOrDefault(field_type->options(), @@ -1638,12 +1109,23 @@ bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) { GetBoolOptionOrDefault(field_type->options(), "map_entry", false); } -void ProtoStreamObjectWriter::WriteTag(const google::protobuf::Field& field) { - WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType( - static_cast<WireFormatLite::FieldType>(field.kind())); - stream_->WriteTag(WireFormatLite::MakeTag(field.number(), wire_type)); +bool ProtoStreamObjectWriter::IsAny(const google::protobuf::Field& field) { + return GetTypeWithoutUrl(field.type_url()) == kAnyType; } +bool ProtoStreamObjectWriter::IsStruct(const google::protobuf::Field& field) { + return GetTypeWithoutUrl(field.type_url()) == kStructType; +} + +bool ProtoStreamObjectWriter::IsStructValue( + const google::protobuf::Field& field) { + return GetTypeWithoutUrl(field.type_url()) == kStructValueType; +} + +bool ProtoStreamObjectWriter::IsStructListValue( + const google::protobuf::Field& field) { + return GetTypeWithoutUrl(field.type_url()) == kStructListValueType; +} } // namespace converter } // namespace util diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.h b/src/google/protobuf/util/internal/protostream_objectwriter.h index 6e133679..08ac6e33 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter.h +++ b/src/google/protobuf/util/internal/protostream_objectwriter.h @@ -42,6 +42,7 @@ #include <google/protobuf/util/internal/type_info.h> #include <google/protobuf/util/internal/datapiece.h> #include <google/protobuf/util/internal/error_listener.h> +#include <google/protobuf/util/internal/proto_writer.h> #include <google/protobuf/util/internal/structured_objectwriter.h> #include <google/protobuf/util/type_resolver.h> #include <google/protobuf/stubs/bytestream.h> @@ -67,9 +68,11 @@ namespace converter { class ObjectLocationTracker; // An ObjectWriter that can write protobuf bytes directly from writer events. +// This class supports all special types like Struct and Map. It uses +// the ProtoWriter class to write raw proto bytes. // // It also supports streaming. -class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter { +class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public ProtoWriter { public: // Constructor. Does not take ownership of any parameter passed in. ProtoStreamObjectWriter(TypeResolver* type_resolver, @@ -82,56 +85,13 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter virtual ProtoStreamObjectWriter* EndObject(); virtual ProtoStreamObjectWriter* StartList(StringPiece name); virtual ProtoStreamObjectWriter* EndList(); - virtual ProtoStreamObjectWriter* RenderBool(StringPiece name, bool value) { - return RenderDataPiece(name, DataPiece(value)); - } - virtual ProtoStreamObjectWriter* RenderInt32(StringPiece name, int32 value) { - return RenderDataPiece(name, DataPiece(value)); - } - virtual ProtoStreamObjectWriter* RenderUint32(StringPiece name, - uint32 value) { - return RenderDataPiece(name, DataPiece(value)); - } - virtual ProtoStreamObjectWriter* RenderInt64(StringPiece name, int64 value) { - return RenderDataPiece(name, DataPiece(value)); - } - virtual ProtoStreamObjectWriter* RenderUint64(StringPiece name, - uint64 value) { - return RenderDataPiece(name, DataPiece(value)); - } - virtual ProtoStreamObjectWriter* RenderDouble(StringPiece name, - double value) { - return RenderDataPiece(name, DataPiece(value)); - } - virtual ProtoStreamObjectWriter* RenderFloat(StringPiece name, float value) { - return RenderDataPiece(name, DataPiece(value)); - } - virtual ProtoStreamObjectWriter* RenderString(StringPiece name, - StringPiece value) { - return RenderDataPiece(name, DataPiece(value)); - } - virtual ProtoStreamObjectWriter* RenderBytes(StringPiece name, - StringPiece value) { - return RenderDataPiece(name, DataPiece(value, false)); - } - virtual ProtoStreamObjectWriter* RenderNull(StringPiece name) { - return RenderDataPiece(name, DataPiece::NullData()); - } // Renders a DataPiece 'value' into a field whose wire type is determined // from the given field 'name'. - ProtoStreamObjectWriter* RenderDataPiece(StringPiece name, - const DataPiece& value); + virtual ProtoStreamObjectWriter* RenderDataPiece(StringPiece name, + const DataPiece& value); - // Returns the location tracker to use for tracking locations for errors. - const LocationTrackerInterface& location() { - return element_ != NULL ? *element_ : *tracker_; - } - - // When true, we finished writing to output a complete message. - bool done() const { return done_; } - - private: + protected: // Function that renders a well known type with modified behavior. typedef util::Status (*TypeRenderer)(ProtoStreamObjectWriter*, const DataPiece&); @@ -192,73 +152,37 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter bool has_injected_value_message_; }; - class LIBPROTOBUF_EXPORT ProtoElement : public BaseElement, public LocationTrackerInterface { + // Represents an item in a stack of items used to keep state between + // ObjectWrier events. + class LIBPROTOBUF_EXPORT Item : public BaseElement { public: - // Indicates the type of element. Special types like LIST, MAP, MAP_ENTRY, - // STRUCT etc. are used to deduce other information based on their position - // on the stack of elements. - enum ElementType { - MESSAGE, // Simple message - LIST, // List/repeated element - MAP, // Proto3 map type - MAP_ENTRY, // Proto3 map message type, with 'key' and 'value' fields - ANY, // Proto3 Any type - STRUCT, // Proto3 struct type - STRUCT_VALUE, // Struct's Value message type - STRUCT_LIST, // List type indicator within a struct - STRUCT_LIST_VALUE, // Struct Value's ListValue message type - STRUCT_MAP, // Struct within a struct type - STRUCT_MAP_ENTRY // Struct map's entry type with 'key' and 'value' - // fields + // Indicates the type of item. + enum ItemType { + MESSAGE, // Simple message + MAP, // Proto3 map type + ANY, // Proto3 Any type }; - // Constructor for the root element. No parent nor field. - ProtoElement(const TypeInfo* typeinfo, const google::protobuf::Type& type, - ProtoStreamObjectWriter* enclosing); - - // Constructor for a field of an element. - ProtoElement(ProtoElement* parent, const google::protobuf::Field* field, - const google::protobuf::Type& type, ElementType element_type); + // Constructor for the root item. + Item(ProtoStreamObjectWriter* enclosing, ItemType item_type, + bool is_placeholder, bool is_list); - virtual ~ProtoElement() {} + // Constructor for a field of a message. + Item(Item* parent, ItemType item_type, bool is_placeholder, bool is_list); - // Called just before the destructor for clean up: - // - reports any missing required fields - // - computes the space needed by the size field, and augment the - // length of all parent messages by this additional space. - // - releases and returns the parent pointer. - ProtoElement* pop(); - - // Accessors - const google::protobuf::Field* field() const { return field_; } - const google::protobuf::Type& type() const { return type_; } + virtual ~Item() {} // These functions return true if the element type is corresponding to the // type in function name. - bool IsMap() { return element_type_ == MAP; } - bool IsStructMap() { return element_type_ == STRUCT_MAP; } - bool IsStructMapEntry() { return element_type_ == STRUCT_MAP_ENTRY; } - bool IsStructList() { return element_type_ == STRUCT_LIST; } - bool IsAny() { return element_type_ == ANY; } - - ElementType element_type() { return element_type_; } - - void RegisterField(const google::protobuf::Field* field); - virtual string ToString() const; + bool IsMap() { return item_type_ == MAP; } + bool IsAny() { return item_type_ == ANY; } AnyWriter* any() const { return any_.get(); } - virtual ProtoElement* parent() const { - return static_cast<ProtoElement*>(BaseElement::parent()); + virtual Item* parent() const { + return static_cast<Item*>(BaseElement::parent()); } - // Returns true if the index is already taken by a preceeding oneof input. - bool OneofIndexTaken(int32 index); - - // Marks the oneof 'index' as taken. Future inputs to this oneof will - // generate an error. - void TakeOneofIndex(int32 index); - // Inserts map key into hash set if and only if the key did NOT already // exist in hash set. // The hash set (map_keys_) is ONLY used to keep track of map keys. @@ -266,6 +190,9 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter // already present. bool InsertMapKeyIfNotPresent(StringPiece map_key); + bool is_placeholder() const { return is_placeholder_; } + bool is_list() const { return is_list_; } + private: // Used for access to variables of the enclosing instance of // ProtoStreamObjectWriter. @@ -274,126 +201,42 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter // A writer for Any objects, handles all Any-related nonsense. google::protobuf::scoped_ptr<AnyWriter> any_; - // Describes the element as a field in the parent message. - // field_ is NULL if and only if this element is the root element. - const google::protobuf::Field* field_; - - // TypeInfo to lookup types. - const TypeInfo* typeinfo_; - - // Additional variables if this element is a message: - // (Root element is always a message). - // descriptor_ : describes allowed fields in the message. - // required_fields_: set of required fields. - // is_repeated_type_ : true if the element is of type list or map. - // size_index_ : index into ProtoStreamObjectWriter::size_insert_ - // for later insertion of serialized message length. - const google::protobuf::Type& type_; - std::set<const google::protobuf::Field*> required_fields_; - const bool is_repeated_type_; - const int size_index_; - - // Tracks position in repeated fields, needed for LocationTrackerInterface. - int array_index_; - // The type of this element, see enum for permissible types. - ElementType element_type_; - - // Set of oneof indices already seen for the type_. Used to validate - // incoming messages so no more than one oneof is set. - hash_set<int32> oneof_indices_; + ItemType item_type_; // Set of map keys already seen for the type_. Used to validate incoming // messages so no map key appears more than once. hash_set<string> map_keys_; - GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoElement); - }; + // Conveys whether this Item is a placeholder or not. Placeholder items are + // pushed to stack to account for special types. + bool is_placeholder_; - // Container for inserting 'size' information at the 'pos' position. - struct SizeInfo { - const int pos; - int size; + // Conveys whether this Item is a list or not. This is used to send + // StartList or EndList calls to underlying ObjectWriter. + bool is_list_; + + GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(Item); }; ProtoStreamObjectWriter(const TypeInfo* typeinfo, const google::protobuf::Type& type, strings::ByteSink* output, ErrorListener* listener); - ProtoElement* element() { return element_.get(); } - - // Helper methods for calling ErrorListener. See error_listener.h. - void InvalidName(StringPiece unknown_name, StringPiece message); - void InvalidValue(StringPiece type_name, StringPiece value); - void MissingField(StringPiece missing_name); - - // Common code for BeginObject() and BeginList() that does invalid_depth_ - // bookkeeping associated with name lookup. - const google::protobuf::Field* BeginNamed(StringPiece name, bool is_list); - - // Lookup the field in the current element. Looks in the base descriptor - // and in any extension. This will report an error if the field cannot be - // found or if multiple matching extensions are found. - const google::protobuf::Field* Lookup(StringPiece name); - - // Lookup the field type in the type descriptor. Returns NULL if the type - // is not known. - const google::protobuf::Type* LookupType( - const google::protobuf::Field* field); - - // Looks up the oneof struct Value field depending on the type. - // On failure to find, it returns an appropriate error. - util::StatusOr<const google::protobuf::Field*> LookupStructField( - DataPiece::Type type); - - // Starts an entry in map. This will be called after placing map element at - // the top of the stack. Uses this information to write map entries. - const google::protobuf::Field* StartMapEntry(StringPiece name); - - // Starts a google.protobuf.Struct. - // 'field' is of type google.protobuf.Struct. - // If field is NULL, it indicates that the top-level message is a struct - // type. - void StartStruct(const google::protobuf::Field* field); - - // Starts another struct within a struct. - // 'field' is of type google.protobuf.Value (see struct.proto). - const google::protobuf::Field* StartStructValueInStruct( - const google::protobuf::Field* field); - - // Starts a list within a struct. - // 'field' is of type google.protobuf.ListValue (see struct.proto). - const google::protobuf::Field* StartListValueInStruct( - const google::protobuf::Field* field); - - // Starts the repeated "values" field in struct.proto's - // google.protobuf.ListValue type. 'field' should be of type - // google.protobuf.ListValue. - const google::protobuf::Field* StartRepeatedValuesInListValue( - const google::protobuf::Field* field); - - // Pops sentinel elements off the stack. - void SkipElements(); - - // Write serialized output to the final output ByteSink, inserting all - // the size information for nested messages that are missing from the - // intermediate Cord buffer. - void WriteRootMessage(); - // Returns true if the field is a map. bool IsMap(const google::protobuf::Field& field); // Returns true if the field is an any. bool IsAny(const google::protobuf::Field& field); - // Helper method to write proto tags based on the given field. - void WriteTag(const google::protobuf::Field& field); + // Returns true if the field is google.protobuf.Struct. + bool IsStruct(const google::protobuf::Field& field); + // Returns true if the field is google.protobuf.Value. + bool IsStructValue(const google::protobuf::Field& field); - // Helper function to render primitive data types in DataPiece. - void RenderSimpleDataPiece(const google::protobuf::Field& field, - const google::protobuf::Type& type, - const DataPiece& data); + // Returns true if the field is google.protobuf.ListValue. + bool IsStructListValue(const google::protobuf::Field& field); // Renders google.protobuf.Value in struct.proto. It picks the right oneof // type based on value's type. @@ -417,69 +260,46 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter static util::Status RenderWrapperType(ProtoStreamObjectWriter* ow, const DataPiece& value); - // Helper functions to create the map and find functions responsible for - // rendering well known types, keyed by type URL. - static hash_map<string, TypeRenderer>* renderers_; static void InitRendererMap(); static void DeleteRendererMap(); static TypeRenderer* FindTypeRenderer(const string& type_url); - // Returns the ProtoElement::ElementType for the given Type. - static ProtoElement::ElementType GetElementType( - const google::protobuf::Type& type); - - // Returns true if the field for type_ can be set as a oneof. If field is not - // a oneof type, this function does nothing and returns true. - // If another field for this oneof is already set, this function returns - // false. It also calls the appropriate error callback. - // unnormalized_name is used for error string. - bool ValidOneof(const google::protobuf::Field& field, - StringPiece unnormalized_name); - // Returns true if the map key for type_ is not duplicated key. // If map key is duplicated key, this function returns false. - // Note that caller should make sure that the current proto element (element_) + // Note that caller should make sure that the current proto element (current_) // is of element type MAP or STRUCT_MAP. // It also calls the appropriate error callback and unnormalzied_name is used // for error string. bool ValidMapKey(StringPiece unnormalized_name); + // Pushes an item on to the stack. Also calls either StartObject or StartList + // on the underlying ObjectWriter depending on whether is_list is false or + // not. + // is_placeholder conveys whether the item is a placeholder item or not. + // Placeholder items are pushed when adding auxillary types' StartObject or + // StartList calls. + void Push(StringPiece name, Item::ItemType item_type, bool is_placeholder, + bool is_list); + + // Pops items from the stack. All placeholder items are popped until a + // non-placeholder item is found. + void Pop(); + + // Pops one element from the stack. Calls EndObject() or EndList() on the + // underlying ObjectWriter depending on the value of is_list_. + void PopOneElement(); + + private: + // Helper functions to create the map and find functions responsible for + // rendering well known types, keyed by type URL. + static hash_map<string, TypeRenderer>* renderers_; + // Variables for describing the structure of the input tree: // master_type_: descriptor for the whole protobuf message. - // typeinfo_ : the TypeInfo object to lookup types. const google::protobuf::Type& master_type_; - const TypeInfo* typeinfo_; - // Whether we own the typeinfo_ object. - bool own_typeinfo_; - - // Indicates whether we finished writing root message completely. - bool done_; - - // Variable for internal state processing: - // element_ : the current element. - // size_insert_: sizes of nested messages. - // pos - position to insert the size field. - // size - size value to be inserted. - google::protobuf::scoped_ptr<ProtoElement> element_; - std::deque<SizeInfo> size_insert_; - - // Variables for output generation: - // output_ : pointer to an external ByteSink for final user-visible output. - // buffer_ : buffer holding partial message before being ready for output_. - // adapter_ : internal adapter between CodedOutputStream and Cord buffer_. - // stream_ : wrapper for writing tags and other encodings in wire format. - strings::ByteSink* output_; - string buffer_; - google::protobuf::io::StringOutputStream adapter_; - google::protobuf::scoped_ptr<google::protobuf::io::CodedOutputStream> stream_; - - // Variables for error tracking and reporting: - // listener_ : a place to report any errors found. - // invalid_depth_: number of enclosing invalid nested messages. - // tracker_ : the root location tracker interface. - ErrorListener* listener_; - int invalid_depth_; - google::protobuf::scoped_ptr<LocationTrackerInterface> tracker_; + + // The current element, variable for internal state processing. + google::protobuf::scoped_ptr<Item> current_; 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 cb9f97f7..c7667c21 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter_test.cc +++ b/src/google/protobuf/util/internal/protostream_objectwriter_test.cc @@ -241,6 +241,21 @@ TEST_P(ProtoStreamObjectWriterTest, SimpleMessage) { CheckOutput(book); } +TEST_P(ProtoStreamObjectWriterTest, CustomJsonName) { + Book book; + Author* robert = book.mutable_author(); + robert->set_id(12345); + robert->set_name("robert"); + + ow_->StartObject("") + ->StartObject("author") + ->RenderUint64("@id", 12345) + ->RenderString("name", "robert") + ->EndObject() + ->EndObject(); + CheckOutput(book); +} + TEST_P(ProtoStreamObjectWriterTest, PrimitiveFromStringConversion) { Primitive full; full.set_fix32(101); @@ -796,10 +811,6 @@ TEST_P(ProtoStreamObjectWriterTest, RootNamedList) { InvalidName(_, StringPiece("oops"), StringPiece("Root element should not be named."))) .With(Args<0>(HasObjectLocation(""))); - EXPECT_CALL(listener_, - InvalidName(_, StringPiece(""), - StringPiece("Proto fields must have a name."))) - .With(Args<0>(HasObjectLocation(""))); ow_->StartList("oops")->RenderString("", "item")->EndList(); CheckOutput(empty, 0); } @@ -864,6 +875,18 @@ INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, ::testing::Values( testing::USE_TYPE_RESOLVER)); +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, ParseTimestamp) { + TimestampDuration timestamp; + google::protobuf::Timestamp* ts = timestamp.mutable_ts(); + ts->set_seconds(1448249855); + ts->set_nanos(33155000); + + ow_->StartObject("") + ->RenderString("ts", "2015-11-23T03:37:35.033155Z") + ->EndObject(); + CheckOutput(timestamp); +} + TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError1) { TimestampDuration timestamp; @@ -922,9 +945,66 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError4) { CheckOutput(timestamp); } -// TODO(skarvaje): Write a test for nanos that exceed limit. Currently, it is -// not possible to construct a test case where nanos exceed limit because of -// floating point arithmetic. +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError5) { + TimestampDuration timestamp; + + EXPECT_CALL( + listener_, + InvalidValue(_, + StringPiece("type.googleapis.com/google.protobuf.Timestamp"), + StringPiece("Field 'ts', Invalid time format: " + "2015-11-23T03:37:35.033155 Z"))); + + ow_->StartObject("") + // Whitespace in the Timestamp nanos is not allowed. + ->RenderString("ts", "2015-11-23T03:37:35.033155 Z") + ->EndObject(); + CheckOutput(timestamp); +} + +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError6) { + TimestampDuration timestamp; + + EXPECT_CALL( + listener_, + InvalidValue(_, + StringPiece("type.googleapis.com/google.protobuf.Timestamp"), + StringPiece("Field 'ts', Invalid time format: " + "2015-11-23T03:37:35.033155 1234Z"))); + + ow_->StartObject("") + // Whitespace in the Timestamp nanos is not allowed. + ->RenderString("ts", "2015-11-23T03:37:35.033155 1234Z") + ->EndObject(); + CheckOutput(timestamp); +} + +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError7) { + TimestampDuration timestamp; + + EXPECT_CALL( + listener_, + InvalidValue(_, + StringPiece("type.googleapis.com/google.protobuf.Timestamp"), + StringPiece("Field 'ts', Invalid time format: " + "2015-11-23T03:37:35.033abc155Z"))); + + ow_->StartObject("") + // Non-numeric characters in the Timestamp nanos is not allowed. + ->RenderString("ts", "2015-11-23T03:37:35.033abc155Z") + ->EndObject(); + CheckOutput(timestamp); +} + +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, ParseDuration) { + TimestampDuration duration; + google::protobuf::Duration* dur = duration.mutable_dur(); + dur->set_seconds(1448216930); + dur->set_nanos(132262000); + + ow_->StartObject("")->RenderString("dur", "1448216930.132262s")->EndObject(); + CheckOutput(duration); +} TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError1) { TimestampDuration duration; @@ -962,7 +1042,7 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError3) { InvalidValue( _, StringPiece("type.googleapis.com/google.protobuf.Duration"), StringPiece("Field 'dur', Invalid duration format, failed to " - "parse nanos seconds"))); + "parse nano seconds"))); ow_->StartObject("")->RenderString("dur", "123.DEFs")->EndObject(); CheckOutput(duration); @@ -981,6 +1061,19 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError4) { CheckOutput(duration); } +TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError5) { + TimestampDuration duration; + + EXPECT_CALL( + listener_, + InvalidValue(_, + StringPiece("type.googleapis.com/google.protobuf.Duration"), + StringPiece("Field 'dur', Duration value exceeds limits"))); + + ow_->StartObject("")->RenderString("dur", "0.1000000001s")->EndObject(); + CheckOutput(duration); +} + TEST_P(ProtoStreamObjectWriterTimestampDurationTest, MismatchedTimestampTypeInput) { TimestampDuration timestamp; @@ -1066,12 +1159,12 @@ TEST_P(ProtoStreamObjectWriterStructTest, StructInvalidInputFailure) { TEST_P(ProtoStreamObjectWriterStructTest, SimpleRepeatedStructMapKeyTest) { EXPECT_CALL( listener_, - InvalidName(_, StringPiece("k1"), - StringPiece("Repeated map key: 'k1' is already set."))); + InvalidName(_, StringPiece("gBike"), + StringPiece("Repeated map key: 'gBike' is already set."))); ow_->StartObject("") ->StartObject("object") - ->RenderString("k1", "v1") - ->RenderString("k1", "v2") + ->RenderString("gBike", "v1") + ->RenderString("gBike", "v2") ->EndObject() ->EndObject(); } @@ -1121,10 +1214,11 @@ INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, TEST_P(ProtoStreamObjectWriterMapTest, MapShouldNotAcceptList) { MapIn mm; - EXPECT_CALL(listener_, - InvalidValue(_, StringPiece("Map"), - StringPiece("Cannot bind a list to map."))) - .With(Args<0>(HasObjectLocation("map_input"))); + EXPECT_CALL( + listener_, + InvalidValue( + _, StringPiece("Map"), + StringPiece("Cannot bind a list to map for field 'map_input'."))); ow_->StartObject("") ->StartList("map_input") ->RenderString("a", "b") diff --git a/src/google/protobuf/util/internal/testdata/books.proto b/src/google/protobuf/util/internal/testdata/books.proto index 6e2f109b..82b81760 100644 --- a/src/google/protobuf/util/internal/testdata/books.proto +++ b/src/google/protobuf/util/internal/testdata/books.proto @@ -66,7 +66,7 @@ message Publisher { // An author of a book message Author { - optional uint64 id = 1; + optional uint64 id = 1 [json_name = "@id"]; optional string name = 2; repeated string pseudonym = 3; optional bool alive = 4; diff --git a/src/google/protobuf/util/internal/testdata/default_value.proto b/src/google/protobuf/util/internal/testdata/default_value.proto index ebbdf6ab..cccc741c 100644 --- a/src/google/protobuf/util/internal/testdata/default_value.proto +++ b/src/google/protobuf/util/internal/testdata/default_value.proto @@ -75,9 +75,10 @@ message DefaultValueTestCases { IntToStringMap int_to_string = 403; MixedMap mixed1 = 404; MixedMap2 mixed2 = 405; - MessageMap map_of_objects = 406; - MixedMap mixed_empty = 407; - MessageMap message_map_empty = 408; + MixedMap2 empty_mixed2 = 406; + MessageMap map_of_objects = 407; + MixedMap mixed_empty = 408; + MessageMap message_map_empty = 409; DoubleValueMessage double_value = 501; DoubleValueMessage double_value_default = 502; } diff --git a/src/google/protobuf/util/internal/testdata/default_value_test.proto b/src/google/protobuf/util/internal/testdata/default_value_test.proto index 21b85e6d..93288341 100644 --- a/src/google/protobuf/util/internal/testdata/default_value_test.proto +++ b/src/google/protobuf/util/internal/testdata/default_value_test.proto @@ -43,4 +43,11 @@ message DefaultValueTest { bool bool_value = 13; string string_value = 15; bytes bytes_value = 17 [ctype = CORD]; + + enum EnumDefault { + ENUM_FIRST = 0; + ENUM_SECOND = 1; + ENUM_THIRD = 2; + } + EnumDefault enum_value = 18; } diff --git a/src/google/protobuf/util/internal/type_info.cc b/src/google/protobuf/util/internal/type_info.cc index a45a76e3..00a8ee7a 100644 --- a/src/google/protobuf/util/internal/type_info.cc +++ b/src/google/protobuf/util/internal/type_info.cc @@ -136,8 +136,7 @@ class TypeInfoForTypeResolver : public TypeInfo { for (int i = 0; i < type->fields_size(); ++i) { const google::protobuf::Field& field = type->fields(i); StringPiece name = field.name(); - StringPiece camel_case_name = - *string_storage_.insert(ToCamelCase(name)).first; + StringPiece camel_case_name = field.json_name(); const StringPiece* existing = InsertOrReturnExisting( &camel_case_name_table_, camel_case_name, name); if (existing && *existing != name) { diff --git a/src/google/protobuf/util/internal/utility.cc b/src/google/protobuf/util/internal/utility.cc index 61899c24..1ddf2487 100644 --- a/src/google/protobuf/util/internal/utility.cc +++ b/src/google/protobuf/util/internal/utility.cc @@ -49,7 +49,8 @@ namespace converter { namespace { const StringPiece SkipWhiteSpace(StringPiece str) { StringPiece::size_type i; - for (i = 0; i < str.size() && isspace(str[i]); ++i) {} + for (i = 0; i < str.size() && isspace(str[i]); ++i) { + } GOOGLE_DCHECK(i == str.size() || !isspace(str[i])); return StringPiece(str, i); } @@ -160,6 +161,19 @@ const google::protobuf::Field* FindFieldInTypeOrNull( return NULL; } +const google::protobuf::Field* FindJsonFieldInTypeOrNull( + const google::protobuf::Type* type, StringPiece json_name) { + if (type != NULL) { + for (int i = 0; i < type->fields_size(); ++i) { + const google::protobuf::Field& field = type->fields(i); + if (field.json_name() == json_name) { + return &field; + } + } + } + return NULL; +} + const google::protobuf::EnumValue* FindEnumValueByNameOrNull( const google::protobuf::Enum* enum_type, StringPiece enum_name) { if (enum_type != NULL) { @@ -316,16 +330,23 @@ string FloatAsString(float value) { return DoubleAsString(value); } -bool SafeStrToFloat(StringPiece str, float *value) { +bool SafeStrToFloat(StringPiece str, float* value) { double double_value; if (!safe_strtod(str, &double_value)) { return false; } - *value = static_cast<float>(double_value); - if (MathLimits<float>::IsInf(*value)) { + if (MathLimits<double>::IsInf(double_value) || + MathLimits<double>::IsNaN(double_value)) + return false; + + // Fail if the value is not representable in float. + if (double_value > std::numeric_limits<float>::max() || + double_value < -std::numeric_limits<float>::max()) { return false; } + + *value = static_cast<float>(double_value); return true; } diff --git a/src/google/protobuf/util/internal/utility.h b/src/google/protobuf/util/internal/utility.h index 5ba97bd2..33df8eda 100644 --- a/src/google/protobuf/util/internal/utility.h +++ b/src/google/protobuf/util/internal/utility.h @@ -127,6 +127,11 @@ const google::protobuf::Option* FindOptionOrNull( const google::protobuf::Field* FindFieldInTypeOrNull( const google::protobuf::Type* type, StringPiece field_name); +// Similar to FindFieldInTypeOrNull, but this looks up fields with given +// json_name. +const google::protobuf::Field* FindJsonFieldInTypeOrNull( + const google::protobuf::Type* type, StringPiece json_name); + // Finds and returns the EnumValue identified by enum_name in the passed tech // Enum object. Returns NULL if none found. const google::protobuf::EnumValue* FindEnumValueByNameOrNull( diff --git a/src/google/protobuf/util/json_format_proto3.proto b/src/google/protobuf/util/json_format_proto3.proto index e8137677..a1e24c18 100644 --- a/src/google/protobuf/util/json_format_proto3.proto +++ b/src/google/protobuf/util/json_format_proto3.proto @@ -165,3 +165,12 @@ message TestListValue { google.protobuf.ListValue value = 1; repeated google.protobuf.ListValue repeated_value = 2; } + +message TestBoolValue { + bool bool_value = 1; + map<bool, int32> bool_map = 2; +} + +message TestCustomJsonName { + int32 value = 1 [json_name = "@value"]; +} diff --git a/src/google/protobuf/util/message_differencer.cc b/src/google/protobuf/util/message_differencer.cc index 47237e5a..0f879dc7 100644 --- a/src/google/protobuf/util/message_differencer.cc +++ b/src/google/protobuf/util/message_differencer.cc @@ -238,9 +238,25 @@ void MessageDifferencer::TreatAsSet(const FieldDescriptor* field) { GOOGLE_CHECK(key_comparator == NULL) << "Cannot treat this repeated field as both Map and Set for" << " comparison. Field name is: " << field->full_name(); + GOOGLE_CHECK(list_fields_.find(field) == list_fields_.end()) + << "Cannot treat the same field as both SET and LIST. Field name is: " + << field->full_name(); set_fields_.insert(field); } +void MessageDifferencer::TreatAsList(const FieldDescriptor* field) { + GOOGLE_CHECK(field->is_repeated()) << "Field must be repeated: " + << field->full_name(); + const MapKeyComparator* key_comparator = GetMapKeyComparator(field); + GOOGLE_CHECK(key_comparator == NULL) + << "Cannot treat this repeated field as both Map and Set for" + << " comparison. Field name is: " << field->full_name(); + GOOGLE_CHECK(set_fields_.find(field) == set_fields_.end()) + << "Cannot treat the same field as both SET and LIST. Field name is: " + << field->full_name(); + list_fields_.insert(field); +} + void MessageDifferencer::TreatAsMap(const FieldDescriptor* field, const FieldDescriptor* key) { GOOGLE_CHECK(field->is_repeated()) << "Field must be repeated: " @@ -255,6 +271,9 @@ void MessageDifferencer::TreatAsMap(const FieldDescriptor* field, GOOGLE_CHECK(set_fields_.find(field) == set_fields_.end()) << "Cannot treat this repeated field as both Map and Set for " << "comparison."; + GOOGLE_CHECK(list_fields_.find(field) == list_fields_.end()) + << "Cannot treat this repeated field as both Map and List for " + << "comparison."; MapKeyComparator* key_comparator = new MultipleFieldsMapKeyComparator(this, key); owned_key_comparators_.push_back(key_comparator); @@ -920,7 +939,8 @@ bool MessageDifferencer::CheckPathChanged( bool MessageDifferencer::IsTreatedAsSet(const FieldDescriptor* field) { if (!field->is_repeated()) return false; if (field->is_map()) return true; - if (repeated_field_comparison_ == AS_SET) return true; + if (repeated_field_comparison_ == AS_SET) + return list_fields_.find(field) == list_fields_.end(); return (set_fields_.find(field) != set_fields_.end()); } diff --git a/src/google/protobuf/util/message_differencer.h b/src/google/protobuf/util/message_differencer.h index 34c173db..3ea74e67 100644 --- a/src/google/protobuf/util/message_differencer.h +++ b/src/google/protobuf/util/message_differencer.h @@ -397,9 +397,16 @@ class LIBPROTOBUF_EXPORT MessageDifferencer { // + n^3) in which n^3 is the time complexity of the maximum matching // algorithm. // - // REQUIRES: field->is_repeated() + // REQUIRES: field->is_repeated() and field not registered with TreatAsList void TreatAsSet(const FieldDescriptor* field); + // The elements of the given repeated field will be treated as a list for + // diffing purposes, so different orderings of the same elements will NOT be + // considered equal. + // + // REQUIRED: field->is_repeated() and field not registered with TreatAsSet + void TreatAsList(const FieldDescriptor* field); + // The elements of the given repeated field will be treated as a map for // diffing purposes, with |key| being the map key. Thus, elements with the // same key will be compared even if they do not appear at the same index. @@ -791,6 +798,7 @@ class LIBPROTOBUF_EXPORT MessageDifferencer { RepeatedFieldComparison repeated_field_comparison_; FieldSet set_fields_; + FieldSet list_fields_; // Keeps track of MapKeyComparators that are created within // MessageDifferencer. These MapKeyComparators should be deleted // before MessageDifferencer is destroyed. diff --git a/src/google/protobuf/util/message_differencer_unittest.cc b/src/google/protobuf/util/message_differencer_unittest.cc index 701b94ae..4e9ca348 100755 --- a/src/google/protobuf/util/message_differencer_unittest.cc +++ b/src/google/protobuf/util/message_differencer_unittest.cc @@ -1103,12 +1103,19 @@ TEST(MessageDifferencerTest, RepeatedFieldSetTest_Combination) { msg1.add_rw("change"); msg2.add_rw("change"); // Compare - util::MessageDifferencer differencer; - differencer.TreatAsMap(msg1.GetDescriptor()->FindFieldByName("item"), - item->GetDescriptor()->FindFieldByName("a")); - differencer.TreatAsSet(msg1.GetDescriptor()->FindFieldByName("rv")); - differencer.TreatAsSet(item->GetDescriptor()->FindFieldByName("ra")); - EXPECT_TRUE(differencer.Compare(msg1, msg2)); + util::MessageDifferencer differencer1; + differencer1.TreatAsMap(msg1.GetDescriptor()->FindFieldByName("item"), + item->GetDescriptor()->FindFieldByName("a")); + differencer1.TreatAsSet(msg1.GetDescriptor()->FindFieldByName("rv")); + differencer1.TreatAsSet(item->GetDescriptor()->FindFieldByName("ra")); + EXPECT_TRUE(differencer1.Compare(msg1, msg2)); + + util::MessageDifferencer differencer2; + differencer2.TreatAsMap(msg1.GetDescriptor()->FindFieldByName("item"), + item->GetDescriptor()->FindFieldByName("a")); + differencer2.set_repeated_field_comparison(util::MessageDifferencer::AS_SET); + differencer2.TreatAsList(msg1.GetDescriptor()->FindFieldByName("rw")); + EXPECT_TRUE(differencer2.Compare(msg1, msg2)); } TEST(MessageDifferencerTest, RepeatedFieldMapTest_Partial) { @@ -1168,6 +1175,11 @@ TEST(MessageDifferencerTest, RepeatedFieldSetTest_Duplicates) { differencer.TreatAsSet(GetFieldDescriptor(a, "rv")); EXPECT_TRUE(differencer.Compare(b, a)); EXPECT_FALSE(differencer.Compare(c, a)); + + util::MessageDifferencer differencer1; + differencer1.set_repeated_field_comparison(util::MessageDifferencer::AS_SET); + EXPECT_TRUE(differencer1.Compare(b, a)); + EXPECT_FALSE(differencer1.Compare(c, a)); } TEST(MessageDifferencerTest, RepeatedFieldSetTest_PartialSimple) { diff --git a/src/google/protobuf/util/type_resolver_util.cc b/src/google/protobuf/util/type_resolver_util.cc index a0996954..96393903 100644 --- a/src/google/protobuf/util/type_resolver_util.cc +++ b/src/google/protobuf/util/type_resolver_util.cc @@ -159,7 +159,10 @@ class DescriptorPoolTypeResolver : public TypeResolver { } field->set_number(descriptor->number()); field->set_name(descriptor->name()); - field->set_json_name(converter::ToCamelCase(descriptor->name())); + field->set_json_name(descriptor->json_name()); + if (descriptor->has_default_value()) { + field->set_default_value(DefaultValueAsString(descriptor)); + } if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) { field->set_type_url(GetTypeUrl(descriptor->message_type())); } else if (descriptor->type() == FieldDescriptor::TYPE_ENUM) { @@ -200,6 +203,46 @@ class DescriptorPoolTypeResolver : public TypeResolver { return url_prefix_ + "/" + descriptor->full_name(); } + string DefaultValueAsString(const FieldDescriptor* descriptor) { + switch (descriptor->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + return SimpleItoa(descriptor->default_value_int32()); + break; + case FieldDescriptor::CPPTYPE_INT64: + return SimpleItoa(descriptor->default_value_int64()); + break; + case FieldDescriptor::CPPTYPE_UINT32: + return SimpleItoa(descriptor->default_value_uint32()); + break; + case FieldDescriptor::CPPTYPE_UINT64: + return SimpleItoa(descriptor->default_value_uint64()); + break; + case FieldDescriptor::CPPTYPE_FLOAT: + return SimpleFtoa(descriptor->default_value_float()); + break; + case FieldDescriptor::CPPTYPE_DOUBLE: + return SimpleDtoa(descriptor->default_value_double()); + break; + case FieldDescriptor::CPPTYPE_BOOL: + return descriptor->default_value_bool() ? "true" : "false"; + break; + case FieldDescriptor::CPPTYPE_STRING: + if (descriptor->type() == FieldDescriptor::TYPE_BYTES) { + return CEscape(descriptor->default_value_string()); + } else { + return descriptor->default_value_string(); + } + break; + case FieldDescriptor::CPPTYPE_ENUM: + return descriptor->default_value_enum()->name(); + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + GOOGLE_LOG(DFATAL) << "Messages can't have default values!"; + break; + } + return ""; + } + string url_prefix_; const DescriptorPool* pool_; }; diff --git a/src/google/protobuf/util/type_resolver_util_test.cc b/src/google/protobuf/util/type_resolver_util_test.cc index 74b2d0da..8a0bf652 100644 --- a/src/google/protobuf/util/type_resolver_util_test.cc +++ b/src/google/protobuf/util/type_resolver_util_test.cc @@ -43,6 +43,7 @@ #include <google/protobuf/map_unittest.pb.h> #include <google/protobuf/test_util.h> #include <google/protobuf/unittest.pb.h> +#include <google/protobuf/util/json_format_proto3.pb.h> #include <google/protobuf/util/type_resolver.h> #include <google/protobuf/testing/googletest.h> #include <gtest/gtest.h> @@ -332,6 +333,19 @@ TEST_F(DescriptorPoolTypeResolverTest, TestEnum) { EnumHasValue(type, "NEG", -1); } +TEST_F(DescriptorPoolTypeResolverTest, TestJsonName) { + Type type; + ASSERT_TRUE(resolver_->ResolveMessageType( + GetTypeUrl<protobuf_unittest::TestAllTypes>(), &type) + .ok()); + EXPECT_EQ("optionalInt32", FindField(type, "optional_int32")->json_name()); + + ASSERT_TRUE(resolver_->ResolveMessageType( + GetTypeUrl<proto3::TestCustomJsonName>(), &type) + .ok()); + EXPECT_EQ("@value", FindField(type, "value")->json_name()); +} + } // namespace } // namespace util } // namespace protobuf diff --git a/src/google/protobuf/wrappers.pb.cc b/src/google/protobuf/wrappers.pb.cc index e153687b..212dd219 100644 --- a/src/google/protobuf/wrappers.pb.cc +++ b/src/google/protobuf/wrappers.pb.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <google/protobuf/stubs/common.h> +#include <google/protobuf/stubs/port.h> #include <google/protobuf/stubs/once.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/wire_format_lite_inl.h> @@ -262,10 +263,10 @@ 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(\014BP\n\023com" - ".google.protobufB\rWrappersProtoP\001\240\001\001\242\002\003G" - "PB\252\002\036Google.Protobuf.WellKnownTypesb\006pro" - "to3", 403); + " \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); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/wrappers.proto", &protobuf_RegisterTypes); DoubleValue::default_instance_ = new DoubleValue(); @@ -318,6 +319,14 @@ DoubleValue::DoubleValue() // @@protoc_insertion_point(constructor:google.protobuf.DoubleValue) } +DoubleValue::DoubleValue(::google::protobuf::Arena* arena) + : ::google::protobuf::Message(), + _internal_metadata_(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:google.protobuf.DoubleValue) +} + void DoubleValue::InitAsDefaultInstance() { _is_default_instance_ = true; } @@ -342,10 +351,20 @@ DoubleValue::~DoubleValue() { } void DoubleValue::SharedDtor() { + if (GetArenaNoVirtual() != NULL) { + return; + } + if (this != default_instance_) { } } +void DoubleValue::ArenaDtor(void* object) { + DoubleValue* _this = reinterpret_cast< DoubleValue* >(object); + (void)_this; +} +void DoubleValue::RegisterArenaDtor(::google::protobuf::Arena* arena) { +} void DoubleValue::SetCachedSize(int size) const { GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = size; @@ -364,11 +383,7 @@ const DoubleValue& DoubleValue::default_instance() { DoubleValue* DoubleValue::default_instance_ = NULL; DoubleValue* DoubleValue::New(::google::protobuf::Arena* arena) const { - DoubleValue* n = new DoubleValue; - if (arena != NULL) { - arena->Own(n); - } - return n; + return ::google::protobuf::Arena::CreateMessage<DoubleValue>(arena); } void DoubleValue::Clear() { @@ -495,6 +510,18 @@ bool DoubleValue::IsInitialized() const { void DoubleValue::Swap(DoubleValue* other) { if (other == this) return; + if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) { + InternalSwap(other); + } else { + DoubleValue temp; + temp.MergeFrom(*this); + CopyFrom(*other); + other->CopyFrom(temp); + } +} +void DoubleValue::UnsafeArenaSwap(DoubleValue* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual()); InternalSwap(other); } void DoubleValue::InternalSwap(DoubleValue* other) { @@ -542,6 +569,14 @@ FloatValue::FloatValue() // @@protoc_insertion_point(constructor:google.protobuf.FloatValue) } +FloatValue::FloatValue(::google::protobuf::Arena* arena) + : ::google::protobuf::Message(), + _internal_metadata_(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:google.protobuf.FloatValue) +} + void FloatValue::InitAsDefaultInstance() { _is_default_instance_ = true; } @@ -566,10 +601,20 @@ FloatValue::~FloatValue() { } void FloatValue::SharedDtor() { + if (GetArenaNoVirtual() != NULL) { + return; + } + if (this != default_instance_) { } } +void FloatValue::ArenaDtor(void* object) { + FloatValue* _this = reinterpret_cast< FloatValue* >(object); + (void)_this; +} +void FloatValue::RegisterArenaDtor(::google::protobuf::Arena* arena) { +} void FloatValue::SetCachedSize(int size) const { GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = size; @@ -588,11 +633,7 @@ const FloatValue& FloatValue::default_instance() { FloatValue* FloatValue::default_instance_ = NULL; FloatValue* FloatValue::New(::google::protobuf::Arena* arena) const { - FloatValue* n = new FloatValue; - if (arena != NULL) { - arena->Own(n); - } - return n; + return ::google::protobuf::Arena::CreateMessage<FloatValue>(arena); } void FloatValue::Clear() { @@ -719,6 +760,18 @@ bool FloatValue::IsInitialized() const { void FloatValue::Swap(FloatValue* other) { if (other == this) return; + if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) { + InternalSwap(other); + } else { + FloatValue temp; + temp.MergeFrom(*this); + CopyFrom(*other); + other->CopyFrom(temp); + } +} +void FloatValue::UnsafeArenaSwap(FloatValue* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual()); InternalSwap(other); } void FloatValue::InternalSwap(FloatValue* other) { @@ -766,6 +819,14 @@ Int64Value::Int64Value() // @@protoc_insertion_point(constructor:google.protobuf.Int64Value) } +Int64Value::Int64Value(::google::protobuf::Arena* arena) + : ::google::protobuf::Message(), + _internal_metadata_(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:google.protobuf.Int64Value) +} + void Int64Value::InitAsDefaultInstance() { _is_default_instance_ = true; } @@ -790,10 +851,20 @@ Int64Value::~Int64Value() { } void Int64Value::SharedDtor() { + if (GetArenaNoVirtual() != NULL) { + return; + } + if (this != default_instance_) { } } +void Int64Value::ArenaDtor(void* object) { + Int64Value* _this = reinterpret_cast< Int64Value* >(object); + (void)_this; +} +void Int64Value::RegisterArenaDtor(::google::protobuf::Arena* arena) { +} void Int64Value::SetCachedSize(int size) const { GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = size; @@ -812,11 +883,7 @@ const Int64Value& Int64Value::default_instance() { Int64Value* Int64Value::default_instance_ = NULL; Int64Value* Int64Value::New(::google::protobuf::Arena* arena) const { - Int64Value* n = new Int64Value; - if (arena != NULL) { - arena->Own(n); - } - return n; + return ::google::protobuf::Arena::CreateMessage<Int64Value>(arena); } void Int64Value::Clear() { @@ -945,6 +1012,18 @@ bool Int64Value::IsInitialized() const { void Int64Value::Swap(Int64Value* other) { if (other == this) return; + if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) { + InternalSwap(other); + } else { + Int64Value temp; + temp.MergeFrom(*this); + CopyFrom(*other); + other->CopyFrom(temp); + } +} +void Int64Value::UnsafeArenaSwap(Int64Value* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual()); InternalSwap(other); } void Int64Value::InternalSwap(Int64Value* other) { @@ -992,6 +1071,14 @@ UInt64Value::UInt64Value() // @@protoc_insertion_point(constructor:google.protobuf.UInt64Value) } +UInt64Value::UInt64Value(::google::protobuf::Arena* arena) + : ::google::protobuf::Message(), + _internal_metadata_(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:google.protobuf.UInt64Value) +} + void UInt64Value::InitAsDefaultInstance() { _is_default_instance_ = true; } @@ -1016,10 +1103,20 @@ UInt64Value::~UInt64Value() { } void UInt64Value::SharedDtor() { + if (GetArenaNoVirtual() != NULL) { + return; + } + if (this != default_instance_) { } } +void UInt64Value::ArenaDtor(void* object) { + UInt64Value* _this = reinterpret_cast< UInt64Value* >(object); + (void)_this; +} +void UInt64Value::RegisterArenaDtor(::google::protobuf::Arena* arena) { +} void UInt64Value::SetCachedSize(int size) const { GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = size; @@ -1038,11 +1135,7 @@ const UInt64Value& UInt64Value::default_instance() { UInt64Value* UInt64Value::default_instance_ = NULL; UInt64Value* UInt64Value::New(::google::protobuf::Arena* arena) const { - UInt64Value* n = new UInt64Value; - if (arena != NULL) { - arena->Own(n); - } - return n; + return ::google::protobuf::Arena::CreateMessage<UInt64Value>(arena); } void UInt64Value::Clear() { @@ -1171,6 +1264,18 @@ bool UInt64Value::IsInitialized() const { void UInt64Value::Swap(UInt64Value* other) { if (other == this) return; + if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) { + InternalSwap(other); + } else { + UInt64Value temp; + temp.MergeFrom(*this); + CopyFrom(*other); + other->CopyFrom(temp); + } +} +void UInt64Value::UnsafeArenaSwap(UInt64Value* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual()); InternalSwap(other); } void UInt64Value::InternalSwap(UInt64Value* other) { @@ -1218,6 +1323,14 @@ Int32Value::Int32Value() // @@protoc_insertion_point(constructor:google.protobuf.Int32Value) } +Int32Value::Int32Value(::google::protobuf::Arena* arena) + : ::google::protobuf::Message(), + _internal_metadata_(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:google.protobuf.Int32Value) +} + void Int32Value::InitAsDefaultInstance() { _is_default_instance_ = true; } @@ -1242,10 +1355,20 @@ Int32Value::~Int32Value() { } void Int32Value::SharedDtor() { + if (GetArenaNoVirtual() != NULL) { + return; + } + if (this != default_instance_) { } } +void Int32Value::ArenaDtor(void* object) { + Int32Value* _this = reinterpret_cast< Int32Value* >(object); + (void)_this; +} +void Int32Value::RegisterArenaDtor(::google::protobuf::Arena* arena) { +} void Int32Value::SetCachedSize(int size) const { GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = size; @@ -1264,11 +1387,7 @@ const Int32Value& Int32Value::default_instance() { Int32Value* Int32Value::default_instance_ = NULL; Int32Value* Int32Value::New(::google::protobuf::Arena* arena) const { - Int32Value* n = new Int32Value; - if (arena != NULL) { - arena->Own(n); - } - return n; + return ::google::protobuf::Arena::CreateMessage<Int32Value>(arena); } void Int32Value::Clear() { @@ -1397,6 +1516,18 @@ bool Int32Value::IsInitialized() const { void Int32Value::Swap(Int32Value* other) { if (other == this) return; + if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) { + InternalSwap(other); + } else { + Int32Value temp; + temp.MergeFrom(*this); + CopyFrom(*other); + other->CopyFrom(temp); + } +} +void Int32Value::UnsafeArenaSwap(Int32Value* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual()); InternalSwap(other); } void Int32Value::InternalSwap(Int32Value* other) { @@ -1444,6 +1575,14 @@ UInt32Value::UInt32Value() // @@protoc_insertion_point(constructor:google.protobuf.UInt32Value) } +UInt32Value::UInt32Value(::google::protobuf::Arena* arena) + : ::google::protobuf::Message(), + _internal_metadata_(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:google.protobuf.UInt32Value) +} + void UInt32Value::InitAsDefaultInstance() { _is_default_instance_ = true; } @@ -1468,10 +1607,20 @@ UInt32Value::~UInt32Value() { } void UInt32Value::SharedDtor() { + if (GetArenaNoVirtual() != NULL) { + return; + } + if (this != default_instance_) { } } +void UInt32Value::ArenaDtor(void* object) { + UInt32Value* _this = reinterpret_cast< UInt32Value* >(object); + (void)_this; +} +void UInt32Value::RegisterArenaDtor(::google::protobuf::Arena* arena) { +} void UInt32Value::SetCachedSize(int size) const { GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = size; @@ -1490,11 +1639,7 @@ const UInt32Value& UInt32Value::default_instance() { UInt32Value* UInt32Value::default_instance_ = NULL; UInt32Value* UInt32Value::New(::google::protobuf::Arena* arena) const { - UInt32Value* n = new UInt32Value; - if (arena != NULL) { - arena->Own(n); - } - return n; + return ::google::protobuf::Arena::CreateMessage<UInt32Value>(arena); } void UInt32Value::Clear() { @@ -1623,6 +1768,18 @@ bool UInt32Value::IsInitialized() const { void UInt32Value::Swap(UInt32Value* other) { if (other == this) return; + if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) { + InternalSwap(other); + } else { + UInt32Value temp; + temp.MergeFrom(*this); + CopyFrom(*other); + other->CopyFrom(temp); + } +} +void UInt32Value::UnsafeArenaSwap(UInt32Value* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual()); InternalSwap(other); } void UInt32Value::InternalSwap(UInt32Value* other) { @@ -1670,6 +1827,14 @@ BoolValue::BoolValue() // @@protoc_insertion_point(constructor:google.protobuf.BoolValue) } +BoolValue::BoolValue(::google::protobuf::Arena* arena) + : ::google::protobuf::Message(), + _internal_metadata_(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:google.protobuf.BoolValue) +} + void BoolValue::InitAsDefaultInstance() { _is_default_instance_ = true; } @@ -1694,10 +1859,20 @@ BoolValue::~BoolValue() { } void BoolValue::SharedDtor() { + if (GetArenaNoVirtual() != NULL) { + return; + } + if (this != default_instance_) { } } +void BoolValue::ArenaDtor(void* object) { + BoolValue* _this = reinterpret_cast< BoolValue* >(object); + (void)_this; +} +void BoolValue::RegisterArenaDtor(::google::protobuf::Arena* arena) { +} void BoolValue::SetCachedSize(int size) const { GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = size; @@ -1716,11 +1891,7 @@ const BoolValue& BoolValue::default_instance() { BoolValue* BoolValue::default_instance_ = NULL; BoolValue* BoolValue::New(::google::protobuf::Arena* arena) const { - BoolValue* n = new BoolValue; - if (arena != NULL) { - arena->Own(n); - } - return n; + return ::google::protobuf::Arena::CreateMessage<BoolValue>(arena); } void BoolValue::Clear() { @@ -1847,6 +2018,18 @@ bool BoolValue::IsInitialized() const { void BoolValue::Swap(BoolValue* other) { if (other == this) return; + if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) { + InternalSwap(other); + } else { + BoolValue temp; + temp.MergeFrom(*this); + CopyFrom(*other); + other->CopyFrom(temp); + } +} +void BoolValue::UnsafeArenaSwap(BoolValue* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual()); InternalSwap(other); } void BoolValue::InternalSwap(BoolValue* other) { @@ -1894,6 +2077,14 @@ StringValue::StringValue() // @@protoc_insertion_point(constructor:google.protobuf.StringValue) } +StringValue::StringValue(::google::protobuf::Arena* arena) + : ::google::protobuf::Message(), + _internal_metadata_(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:google.protobuf.StringValue) +} + void StringValue::InitAsDefaultInstance() { _is_default_instance_ = true; } @@ -1919,11 +2110,21 @@ StringValue::~StringValue() { } void StringValue::SharedDtor() { - value_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + if (GetArenaNoVirtual() != NULL) { + return; + } + + value_.Destroy(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); if (this != default_instance_) { } } +void StringValue::ArenaDtor(void* object) { + StringValue* _this = reinterpret_cast< StringValue* >(object); + (void)_this; +} +void StringValue::RegisterArenaDtor(::google::protobuf::Arena* arena) { +} void StringValue::SetCachedSize(int size) const { GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = size; @@ -1942,15 +2143,11 @@ const StringValue& StringValue::default_instance() { StringValue* StringValue::default_instance_ = NULL; StringValue* StringValue::New(::google::protobuf::Arena* arena) const { - StringValue* n = new StringValue; - if (arena != NULL) { - arena->Own(n); - } - return n; + return ::google::protobuf::Arena::CreateMessage<StringValue>(arena); } void StringValue::Clear() { - value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } bool StringValue::MergePartialFromCodedStream( @@ -2065,8 +2262,7 @@ void StringValue::MergeFrom(const ::google::protobuf::Message& from) { void StringValue::MergeFrom(const StringValue& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.value().size() > 0) { - - value_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.value_); + set_value(from.value()); } } @@ -2089,6 +2285,18 @@ bool StringValue::IsInitialized() const { void StringValue::Swap(StringValue* other) { if (other == this) return; + if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) { + InternalSwap(other); + } else { + StringValue temp; + temp.MergeFrom(*this); + CopyFrom(*other); + other->CopyFrom(temp); + } +} +void StringValue::UnsafeArenaSwap(StringValue* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual()); InternalSwap(other); } void StringValue::InternalSwap(StringValue* other) { @@ -2110,36 +2318,44 @@ void StringValue::InternalSwap(StringValue* other) { // optional string value = 1; void StringValue::clear_value() { - value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } const ::std::string& StringValue::value() const { // @@protoc_insertion_point(field_get:google.protobuf.StringValue.value) - return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + return value_.Get(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } void StringValue::set_value(const ::std::string& value) { - value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, GetArenaNoVirtual()); // @@protoc_insertion_point(field_set:google.protobuf.StringValue.value) } void StringValue::set_value(const char* value) { - value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArenaNoVirtual()); // @@protoc_insertion_point(field_set_char:google.protobuf.StringValue.value) } - void StringValue::set_value(const char* value, size_t size) { + void StringValue::set_value(const char* value, + size_t size) { - value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast<const char*>(value), size)); + value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast<const char*>(value), size), GetArenaNoVirtual()); // @@protoc_insertion_point(field_set_pointer:google.protobuf.StringValue.value) } ::std::string* StringValue::mutable_value() { // @@protoc_insertion_point(field_mutable:google.protobuf.StringValue.value) - return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + return value_.Mutable(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } ::std::string* StringValue::release_value() { - return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + return value_.Release(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); +} + ::std::string* StringValue::unsafe_arena_release_value() { + GOOGLE_DCHECK(GetArenaNoVirtual() != NULL); + + return value_.UnsafeArenaRelease(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + GetArenaNoVirtual()); } void StringValue::set_allocated_value(::std::string* value) { if (value != NULL) { @@ -2147,7 +2363,20 @@ void StringValue::clear_value() { } else { } - value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + value_.SetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, + GetArenaNoVirtual()); + // @@protoc_insertion_point(field_set_allocated:google.protobuf.StringValue.value) +} + void StringValue::unsafe_arena_set_allocated_value( + ::std::string* value) { + GOOGLE_DCHECK(GetArenaNoVirtual() != NULL); + if (value != NULL) { + + } else { + + } + value_.UnsafeArenaSetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + value, GetArenaNoVirtual()); // @@protoc_insertion_point(field_set_allocated:google.protobuf.StringValue.value) } @@ -2165,6 +2394,14 @@ BytesValue::BytesValue() // @@protoc_insertion_point(constructor:google.protobuf.BytesValue) } +BytesValue::BytesValue(::google::protobuf::Arena* arena) + : ::google::protobuf::Message(), + _internal_metadata_(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:google.protobuf.BytesValue) +} + void BytesValue::InitAsDefaultInstance() { _is_default_instance_ = true; } @@ -2190,11 +2427,21 @@ BytesValue::~BytesValue() { } void BytesValue::SharedDtor() { - value_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + if (GetArenaNoVirtual() != NULL) { + return; + } + + value_.Destroy(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); if (this != default_instance_) { } } +void BytesValue::ArenaDtor(void* object) { + BytesValue* _this = reinterpret_cast< BytesValue* >(object); + (void)_this; +} +void BytesValue::RegisterArenaDtor(::google::protobuf::Arena* arena) { +} void BytesValue::SetCachedSize(int size) const { GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = size; @@ -2213,15 +2460,11 @@ const BytesValue& BytesValue::default_instance() { BytesValue* BytesValue::default_instance_ = NULL; BytesValue* BytesValue::New(::google::protobuf::Arena* arena) const { - BytesValue* n = new BytesValue; - if (arena != NULL) { - arena->Own(n); - } - return n; + return ::google::protobuf::Arena::CreateMessage<BytesValue>(arena); } void BytesValue::Clear() { - value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } bool BytesValue::MergePartialFromCodedStream( @@ -2324,8 +2567,7 @@ void BytesValue::MergeFrom(const ::google::protobuf::Message& from) { void BytesValue::MergeFrom(const BytesValue& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); if (from.value().size() > 0) { - - value_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.value_); + set_value(from.value()); } } @@ -2348,6 +2590,18 @@ bool BytesValue::IsInitialized() const { void BytesValue::Swap(BytesValue* other) { if (other == this) return; + if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) { + InternalSwap(other); + } else { + BytesValue temp; + temp.MergeFrom(*this); + CopyFrom(*other); + other->CopyFrom(temp); + } +} +void BytesValue::UnsafeArenaSwap(BytesValue* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual()); InternalSwap(other); } void BytesValue::InternalSwap(BytesValue* other) { @@ -2369,36 +2623,44 @@ void BytesValue::InternalSwap(BytesValue* other) { // optional bytes value = 1; void BytesValue::clear_value() { - value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } const ::std::string& BytesValue::value() const { // @@protoc_insertion_point(field_get:google.protobuf.BytesValue.value) - return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + return value_.Get(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } void BytesValue::set_value(const ::std::string& value) { - value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, GetArenaNoVirtual()); // @@protoc_insertion_point(field_set:google.protobuf.BytesValue.value) } void BytesValue::set_value(const char* value) { - value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArenaNoVirtual()); // @@protoc_insertion_point(field_set_char:google.protobuf.BytesValue.value) } - void BytesValue::set_value(const void* value, size_t size) { + void BytesValue::set_value(const void* value, + size_t size) { - value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast<const char*>(value), size)); + value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast<const char*>(value), size), GetArenaNoVirtual()); // @@protoc_insertion_point(field_set_pointer:google.protobuf.BytesValue.value) } ::std::string* BytesValue::mutable_value() { // @@protoc_insertion_point(field_mutable:google.protobuf.BytesValue.value) - return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + return value_.Mutable(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } ::std::string* BytesValue::release_value() { - return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + return value_.Release(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); +} + ::std::string* BytesValue::unsafe_arena_release_value() { + GOOGLE_DCHECK(GetArenaNoVirtual() != NULL); + + return value_.UnsafeArenaRelease(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + GetArenaNoVirtual()); } void BytesValue::set_allocated_value(::std::string* value) { if (value != NULL) { @@ -2406,7 +2668,20 @@ void BytesValue::clear_value() { } else { } - value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + value_.SetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, + GetArenaNoVirtual()); + // @@protoc_insertion_point(field_set_allocated:google.protobuf.BytesValue.value) +} + void BytesValue::unsafe_arena_set_allocated_value( + ::std::string* value) { + GOOGLE_DCHECK(GetArenaNoVirtual() != NULL); + if (value != NULL) { + + } else { + + } + value_.UnsafeArenaSetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + value, GetArenaNoVirtual()); // @@protoc_insertion_point(field_set_allocated:google.protobuf.BytesValue.value) } diff --git a/src/google/protobuf/wrappers.pb.h b/src/google/protobuf/wrappers.pb.h index 15bcc7a2..7dca938c 100644 --- a/src/google/protobuf/wrappers.pb.h +++ b/src/google/protobuf/wrappers.pb.h @@ -61,9 +61,14 @@ class LIBPROTOBUF_EXPORT DoubleValue : public ::google::protobuf::Message { return *this; } + inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); } + inline void* GetMaybeArenaPointer() const { + return MaybeArenaPtr(); + } static const ::google::protobuf::Descriptor* descriptor(); static const DoubleValue& default_instance(); + void UnsafeArenaSwap(DoubleValue* other); void Swap(DoubleValue* other); // implements Message ---------------------------------------------- @@ -90,6 +95,11 @@ class LIBPROTOBUF_EXPORT DoubleValue : public ::google::protobuf::Message { void SharedDtor(); void SetCachedSize(int size) const; void InternalSwap(DoubleValue* other); + protected: + explicit DoubleValue(::google::protobuf::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::google::protobuf::Arena* arena); private: inline ::google::protobuf::Arena* GetArenaNoVirtual() const { return _internal_metadata_.arena(); @@ -115,6 +125,9 @@ class LIBPROTOBUF_EXPORT DoubleValue : public ::google::protobuf::Message { private: ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; bool _is_default_instance_; double value_; mutable int _cached_size_; @@ -139,9 +152,14 @@ class LIBPROTOBUF_EXPORT FloatValue : public ::google::protobuf::Message { return *this; } + inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); } + inline void* GetMaybeArenaPointer() const { + return MaybeArenaPtr(); + } static const ::google::protobuf::Descriptor* descriptor(); static const FloatValue& default_instance(); + void UnsafeArenaSwap(FloatValue* other); void Swap(FloatValue* other); // implements Message ---------------------------------------------- @@ -168,6 +186,11 @@ class LIBPROTOBUF_EXPORT FloatValue : public ::google::protobuf::Message { void SharedDtor(); void SetCachedSize(int size) const; void InternalSwap(FloatValue* other); + protected: + explicit FloatValue(::google::protobuf::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::google::protobuf::Arena* arena); private: inline ::google::protobuf::Arena* GetArenaNoVirtual() const { return _internal_metadata_.arena(); @@ -193,6 +216,9 @@ class LIBPROTOBUF_EXPORT FloatValue : public ::google::protobuf::Message { private: ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; bool _is_default_instance_; float value_; mutable int _cached_size_; @@ -217,9 +243,14 @@ class LIBPROTOBUF_EXPORT Int64Value : public ::google::protobuf::Message { return *this; } + inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); } + inline void* GetMaybeArenaPointer() const { + return MaybeArenaPtr(); + } static const ::google::protobuf::Descriptor* descriptor(); static const Int64Value& default_instance(); + void UnsafeArenaSwap(Int64Value* other); void Swap(Int64Value* other); // implements Message ---------------------------------------------- @@ -246,6 +277,11 @@ class LIBPROTOBUF_EXPORT Int64Value : public ::google::protobuf::Message { void SharedDtor(); void SetCachedSize(int size) const; void InternalSwap(Int64Value* other); + protected: + explicit Int64Value(::google::protobuf::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::google::protobuf::Arena* arena); private: inline ::google::protobuf::Arena* GetArenaNoVirtual() const { return _internal_metadata_.arena(); @@ -271,6 +307,9 @@ class LIBPROTOBUF_EXPORT Int64Value : public ::google::protobuf::Message { private: ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; bool _is_default_instance_; ::google::protobuf::int64 value_; mutable int _cached_size_; @@ -295,9 +334,14 @@ class LIBPROTOBUF_EXPORT UInt64Value : public ::google::protobuf::Message { return *this; } + inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); } + inline void* GetMaybeArenaPointer() const { + return MaybeArenaPtr(); + } static const ::google::protobuf::Descriptor* descriptor(); static const UInt64Value& default_instance(); + void UnsafeArenaSwap(UInt64Value* other); void Swap(UInt64Value* other); // implements Message ---------------------------------------------- @@ -324,6 +368,11 @@ class LIBPROTOBUF_EXPORT UInt64Value : public ::google::protobuf::Message { void SharedDtor(); void SetCachedSize(int size) const; void InternalSwap(UInt64Value* other); + protected: + explicit UInt64Value(::google::protobuf::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::google::protobuf::Arena* arena); private: inline ::google::protobuf::Arena* GetArenaNoVirtual() const { return _internal_metadata_.arena(); @@ -349,6 +398,9 @@ class LIBPROTOBUF_EXPORT UInt64Value : public ::google::protobuf::Message { private: ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; bool _is_default_instance_; ::google::protobuf::uint64 value_; mutable int _cached_size_; @@ -373,9 +425,14 @@ class LIBPROTOBUF_EXPORT Int32Value : public ::google::protobuf::Message { return *this; } + inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); } + inline void* GetMaybeArenaPointer() const { + return MaybeArenaPtr(); + } static const ::google::protobuf::Descriptor* descriptor(); static const Int32Value& default_instance(); + void UnsafeArenaSwap(Int32Value* other); void Swap(Int32Value* other); // implements Message ---------------------------------------------- @@ -402,6 +459,11 @@ class LIBPROTOBUF_EXPORT Int32Value : public ::google::protobuf::Message { void SharedDtor(); void SetCachedSize(int size) const; void InternalSwap(Int32Value* other); + protected: + explicit Int32Value(::google::protobuf::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::google::protobuf::Arena* arena); private: inline ::google::protobuf::Arena* GetArenaNoVirtual() const { return _internal_metadata_.arena(); @@ -427,6 +489,9 @@ class LIBPROTOBUF_EXPORT Int32Value : public ::google::protobuf::Message { private: ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; bool _is_default_instance_; ::google::protobuf::int32 value_; mutable int _cached_size_; @@ -451,9 +516,14 @@ class LIBPROTOBUF_EXPORT UInt32Value : public ::google::protobuf::Message { return *this; } + inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); } + inline void* GetMaybeArenaPointer() const { + return MaybeArenaPtr(); + } static const ::google::protobuf::Descriptor* descriptor(); static const UInt32Value& default_instance(); + void UnsafeArenaSwap(UInt32Value* other); void Swap(UInt32Value* other); // implements Message ---------------------------------------------- @@ -480,6 +550,11 @@ class LIBPROTOBUF_EXPORT UInt32Value : public ::google::protobuf::Message { void SharedDtor(); void SetCachedSize(int size) const; void InternalSwap(UInt32Value* other); + protected: + explicit UInt32Value(::google::protobuf::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::google::protobuf::Arena* arena); private: inline ::google::protobuf::Arena* GetArenaNoVirtual() const { return _internal_metadata_.arena(); @@ -505,6 +580,9 @@ class LIBPROTOBUF_EXPORT UInt32Value : public ::google::protobuf::Message { private: ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; bool _is_default_instance_; ::google::protobuf::uint32 value_; mutable int _cached_size_; @@ -529,9 +607,14 @@ class LIBPROTOBUF_EXPORT BoolValue : public ::google::protobuf::Message { return *this; } + inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); } + inline void* GetMaybeArenaPointer() const { + return MaybeArenaPtr(); + } static const ::google::protobuf::Descriptor* descriptor(); static const BoolValue& default_instance(); + void UnsafeArenaSwap(BoolValue* other); void Swap(BoolValue* other); // implements Message ---------------------------------------------- @@ -558,6 +641,11 @@ class LIBPROTOBUF_EXPORT BoolValue : public ::google::protobuf::Message { void SharedDtor(); void SetCachedSize(int size) const; void InternalSwap(BoolValue* other); + protected: + explicit BoolValue(::google::protobuf::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::google::protobuf::Arena* arena); private: inline ::google::protobuf::Arena* GetArenaNoVirtual() const { return _internal_metadata_.arena(); @@ -583,6 +671,9 @@ class LIBPROTOBUF_EXPORT BoolValue : public ::google::protobuf::Message { private: ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; bool _is_default_instance_; bool value_; mutable int _cached_size_; @@ -607,9 +698,14 @@ class LIBPROTOBUF_EXPORT StringValue : public ::google::protobuf::Message { return *this; } + inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); } + inline void* GetMaybeArenaPointer() const { + return MaybeArenaPtr(); + } static const ::google::protobuf::Descriptor* descriptor(); static const StringValue& default_instance(); + void UnsafeArenaSwap(StringValue* other); void Swap(StringValue* other); // implements Message ---------------------------------------------- @@ -636,6 +732,11 @@ class LIBPROTOBUF_EXPORT StringValue : public ::google::protobuf::Message { void SharedDtor(); void SetCachedSize(int size) const; void InternalSwap(StringValue* other); + protected: + explicit StringValue(::google::protobuf::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::google::protobuf::Arena* arena); private: inline ::google::protobuf::Arena* GetArenaNoVirtual() const { return _internal_metadata_.arena(); @@ -661,11 +762,17 @@ class LIBPROTOBUF_EXPORT StringValue : public ::google::protobuf::Message { ::std::string* mutable_value(); ::std::string* release_value(); void set_allocated_value(::std::string* value); + ::std::string* unsafe_arena_release_value(); + void unsafe_arena_set_allocated_value( + ::std::string* value); // @@protoc_insertion_point(class_scope:google.protobuf.StringValue) private: ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; bool _is_default_instance_; ::google::protobuf::internal::ArenaStringPtr value_; mutable int _cached_size_; @@ -690,9 +797,14 @@ class LIBPROTOBUF_EXPORT BytesValue : public ::google::protobuf::Message { return *this; } + inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); } + inline void* GetMaybeArenaPointer() const { + return MaybeArenaPtr(); + } static const ::google::protobuf::Descriptor* descriptor(); static const BytesValue& default_instance(); + void UnsafeArenaSwap(BytesValue* other); void Swap(BytesValue* other); // implements Message ---------------------------------------------- @@ -719,6 +831,11 @@ class LIBPROTOBUF_EXPORT BytesValue : public ::google::protobuf::Message { void SharedDtor(); void SetCachedSize(int size) const; void InternalSwap(BytesValue* other); + protected: + explicit BytesValue(::google::protobuf::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::google::protobuf::Arena* arena); private: inline ::google::protobuf::Arena* GetArenaNoVirtual() const { return _internal_metadata_.arena(); @@ -744,11 +861,17 @@ class LIBPROTOBUF_EXPORT BytesValue : public ::google::protobuf::Message { ::std::string* mutable_value(); ::std::string* release_value(); void set_allocated_value(::std::string* value); + ::std::string* unsafe_arena_release_value(); + void unsafe_arena_set_allocated_value( + ::std::string* value); // @@protoc_insertion_point(class_scope:google.protobuf.BytesValue) private: ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; bool _is_default_instance_; ::google::protobuf::internal::ArenaStringPtr value_; mutable int _cached_size_; @@ -895,36 +1018,44 @@ inline void BoolValue::set_value(bool value) { // optional string value = 1; inline void StringValue::clear_value() { - value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } inline const ::std::string& StringValue::value() const { // @@protoc_insertion_point(field_get:google.protobuf.StringValue.value) - return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + return value_.Get(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline void StringValue::set_value(const ::std::string& value) { - value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, GetArenaNoVirtual()); // @@protoc_insertion_point(field_set:google.protobuf.StringValue.value) } inline void StringValue::set_value(const char* value) { - value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArenaNoVirtual()); // @@protoc_insertion_point(field_set_char:google.protobuf.StringValue.value) } -inline void StringValue::set_value(const char* value, size_t size) { +inline void StringValue::set_value(const char* value, + size_t size) { - value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast<const char*>(value), size)); + value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast<const char*>(value), size), GetArenaNoVirtual()); // @@protoc_insertion_point(field_set_pointer:google.protobuf.StringValue.value) } inline ::std::string* StringValue::mutable_value() { // @@protoc_insertion_point(field_mutable:google.protobuf.StringValue.value) - return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + return value_.Mutable(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } inline ::std::string* StringValue::release_value() { - return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + return value_.Release(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); +} +inline ::std::string* StringValue::unsafe_arena_release_value() { + GOOGLE_DCHECK(GetArenaNoVirtual() != NULL); + + return value_.UnsafeArenaRelease(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + GetArenaNoVirtual()); } inline void StringValue::set_allocated_value(::std::string* value) { if (value != NULL) { @@ -932,7 +1063,20 @@ inline void StringValue::set_allocated_value(::std::string* value) { } else { } - value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + value_.SetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, + GetArenaNoVirtual()); + // @@protoc_insertion_point(field_set_allocated:google.protobuf.StringValue.value) +} +inline void StringValue::unsafe_arena_set_allocated_value( + ::std::string* value) { + GOOGLE_DCHECK(GetArenaNoVirtual() != NULL); + if (value != NULL) { + + } else { + + } + value_.UnsafeArenaSetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + value, GetArenaNoVirtual()); // @@protoc_insertion_point(field_set_allocated:google.protobuf.StringValue.value) } @@ -942,36 +1086,44 @@ inline void StringValue::set_allocated_value(::std::string* value) { // optional bytes value = 1; inline void BytesValue::clear_value() { - value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + value_.ClearToEmpty(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } inline const ::std::string& BytesValue::value() const { // @@protoc_insertion_point(field_get:google.protobuf.BytesValue.value) - return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + return value_.Get(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } inline void BytesValue::set_value(const ::std::string& value) { - value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, GetArenaNoVirtual()); // @@protoc_insertion_point(field_set:google.protobuf.BytesValue.value) } inline void BytesValue::set_value(const char* value) { - value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArenaNoVirtual()); // @@protoc_insertion_point(field_set_char:google.protobuf.BytesValue.value) } -inline void BytesValue::set_value(const void* value, size_t size) { +inline void BytesValue::set_value(const void* value, + size_t size) { - value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast<const char*>(value), size)); + value_.Set(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast<const char*>(value), size), GetArenaNoVirtual()); // @@protoc_insertion_point(field_set_pointer:google.protobuf.BytesValue.value) } inline ::std::string* BytesValue::mutable_value() { // @@protoc_insertion_point(field_mutable:google.protobuf.BytesValue.value) - return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + return value_.Mutable(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); } inline ::std::string* BytesValue::release_value() { - return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + return value_.Release(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); +} +inline ::std::string* BytesValue::unsafe_arena_release_value() { + GOOGLE_DCHECK(GetArenaNoVirtual() != NULL); + + return value_.UnsafeArenaRelease(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + GetArenaNoVirtual()); } inline void BytesValue::set_allocated_value(::std::string* value) { if (value != NULL) { @@ -979,7 +1131,20 @@ inline void BytesValue::set_allocated_value(::std::string* value) { } else { } - value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + value_.SetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value, + GetArenaNoVirtual()); + // @@protoc_insertion_point(field_set_allocated:google.protobuf.BytesValue.value) +} +inline void BytesValue::unsafe_arena_set_allocated_value( + ::std::string* value) { + GOOGLE_DCHECK(GetArenaNoVirtual() != NULL); + if (value != NULL) { + + } else { + + } + value_.UnsafeArenaSetAllocated(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + value, GetArenaNoVirtual()); // @@protoc_insertion_point(field_set_allocated:google.protobuf.BytesValue.value) } diff --git a/src/google/protobuf/wrappers.proto b/src/google/protobuf/wrappers.proto index a1d6e446..040d8a24 100644 --- a/src/google/protobuf/wrappers.proto +++ b/src/google/protobuf/wrappers.proto @@ -37,11 +37,12 @@ syntax = "proto3"; package google.protobuf; -option java_generate_equals_and_hash = true; -option java_multiple_files = true; -option java_outer_classname = "WrappersProto"; -option java_package = "com.google.protobuf"; option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option java_package = "com.google.protobuf"; +option java_outer_classname = "WrappersProto"; +option java_multiple_files = true; +option java_generate_equals_and_hash = true; option objc_class_prefix = "GPB"; // Wrapper message for `double`. |