diff options
Diffstat (limited to 'src/google/protobuf/compiler/cpp/cpp_message.cc')
-rw-r--r-- | src/google/protobuf/compiler/cpp/cpp_message.cc | 911 |
1 files changed, 666 insertions, 245 deletions
diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index 1373ffc2..d9524f64 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -32,6 +32,8 @@ // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. +#include <google/protobuf/compiler/cpp/cpp_message.h> + #include <algorithm> #include <google/protobuf/stubs/hash.h> #include <map> @@ -41,16 +43,18 @@ #endif #include <utility> #include <vector> -#include <google/protobuf/compiler/cpp/cpp_message.h> -#include <google/protobuf/compiler/cpp/cpp_field.h> + #include <google/protobuf/compiler/cpp/cpp_enum.h> #include <google/protobuf/compiler/cpp/cpp_extension.h> +#include <google/protobuf/compiler/cpp/cpp_field.h> #include <google/protobuf/compiler/cpp/cpp_helpers.h> -#include <google/protobuf/stubs/strutil.h> -#include <google/protobuf/io/printer.h> #include <google/protobuf/io/coded_stream.h> -#include <google/protobuf/wire_format.h> +#include <google/protobuf/io/printer.h> #include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/generated_message_table_driven.h> +#include <google/protobuf/wire_format.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/stubs/substitute.h> namespace google { @@ -103,54 +107,6 @@ struct ExtensionRangeSorter { } }; -// Returns true if the "required" restriction check should be ignored for the -// given field. -inline static bool ShouldIgnoreRequiredFieldCheck(const FieldDescriptor* field, - const Options& options) { - return false; -} - -// Returns true if the message type has any required fields. If it doesn't, -// we can optimize out calls to its IsInitialized() method. -// -// already_seen is used to avoid checking the same type multiple times -// (and also to protect against recursion). -static bool HasRequiredFields(const Descriptor* type, const Options& options, - hash_set<const Descriptor*>* already_seen) { - if (already_seen->count(type) > 0) { - // Since the first occurrence of a required field causes the whole - // function to return true, we can assume that if the type is already - // in the cache it didn't have any required fields. - return false; - } - already_seen->insert(type); - - // If the type has extensions, an extension with message type could contain - // required fields, so we have to be conservative and assume such an - // extension exists. - if (type->extension_range_count() > 0) return true; - - for (int i = 0; i < type->field_count(); i++) { - const FieldDescriptor* field = type->field(i); - if (field->is_required()) { - return true; - } - if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && - !ShouldIgnoreRequiredFieldCheck(field, options)) { - if (HasRequiredFields(field->message_type(), options, already_seen)) { - return true; - } - } - } - - return false; -} - -static bool HasRequiredFields(const Descriptor* type, const Options& options) { - hash_set<const Descriptor*> already_seen; - return HasRequiredFields(type, options, &already_seen); -} - // This returns an estimate of the compiler's alignment for the field. This // can't guarantee to be correct because the generated code could be compiled on // different systems with different alignment rules. The estimates below assume @@ -288,10 +244,13 @@ bool CanConstructByZeroing(const FieldDescriptor* field, // order, fields of similiar family (see below) are together and within each // family, alignment padding is minimized. // -// We try to do this while keeping each field as close as possible to its -// declaration order (from the .proto file) so that we don't reduce cache -// locality much for function that access each field in order. This is also the -// only (weak) signal we have for author intent concerning field layout. +// We try to do this while keeping each field as close as possible to its field +// number order so that we don't reduce cache locality much for function that +// access each field in order. Originally, OptimizePadding used declaration +// order for its decisions, but generated code minus the serializer/parsers uses +// the output of OptimizePadding as well (stored in +// MessageGenerator::optimized_order_). Since the serializers use field number +// order, we use that as a tie-breaker. // // TODO(ckennelly): If/when we have profiles available for the compiler, use // those rather than respect declaration order. @@ -346,10 +305,11 @@ void OptimizePadding(std::vector<const FieldDescriptor*>* fields, f = ZERO_INITIALIZABLE; } + const int j = field->number(); switch (EstimateAlignmentSize(field)) { - case 1: aligned_to_1[f].push_back(FieldGroup(i, field)); break; - case 4: aligned_to_4[f].push_back(FieldGroup(i, field)); break; - case 8: aligned_to_8[f].push_back(FieldGroup(i, field)); break; + case 1: aligned_to_1[f].push_back(FieldGroup(j, field)); break; + case 4: aligned_to_4[f].push_back(FieldGroup(j, field)); break; + case 8: aligned_to_8[f].push_back(FieldGroup(j, field)); break; default: GOOGLE_LOG(FATAL) << "Unknown alignment size."; } @@ -366,7 +326,7 @@ void OptimizePadding(std::vector<const FieldDescriptor*>* fields, } aligned_to_4[f].push_back(field_group); } - // Sort by preferred location to keep fields as close to their declaration + // Sort by preferred location to keep fields as close to their field number // order as possible. Using stable_sort ensures that the output is // consistent across runs. std::stable_sort(aligned_to_4[f].begin(), aligned_to_4[f].end()); @@ -406,9 +366,6 @@ void OptimizePadding(std::vector<const FieldDescriptor*>* fields, } } -string MessageTypeProtoName(const FieldDescriptor* field) { - return field->message_type()->full_name(); -} // Emits an if-statement with a condition that evaluates to true if |field| is // considered non-default (will be sent over the wire), for message types @@ -495,30 +452,99 @@ bool HasPrivateHasMethod(const FieldDescriptor* field) { } +bool TableDrivenEnabled(const Descriptor* descriptor, const Options& options) { + if (!options.table_driven_parsing) { + return false; + } + + // Consider table-driven parsing. We only do this if: + // - There are no extensions + if (descriptor->extension_range_count() != 0) { + return false; + } + + // - We are not using UnknownFieldSet (part of the non-lite library). + if (UseUnknownFieldSet(descriptor->file(), options)) { + return false; + } + + // - We have has_bits for fields. This avoids a check on every field we set + // when are present (the common case). + if (!HasFieldPresence(descriptor->file())) { + return false; + } + + const double table_sparseness = 0.5; + int max_field_number = 0; + for (int i = 0; i < descriptor->field_count(); i++) { + const FieldDescriptor* field = descriptor->field(i); + if (max_field_number < field->number()) { + max_field_number = field->number(); + } + + // - There are no map fields. + if (field->is_map()) { + return false; + } + + // - There are no oneof fields. + if (field->containing_oneof()) { + return false; + } + + // - There are no weak fields. + if (field->options().weak()) { + return false; + } + } + + // - There range of field numbers is "small" + if (max_field_number >= (2 << 14)) { + return false; + } + + // - Field numbers are relatively dense within the actual number of fields + if (max_field_number * table_sparseness >= descriptor->field_count()) { + return false; + } + + // - This is not a MapEntryMessage. + if (IsMapEntryMessage(descriptor)) { + return false; + } + + return true; +} + } // anonymous namespace // =================================================================== MessageGenerator::MessageGenerator(const Descriptor* descriptor, - const Options& options) + const Options& options, + SCCAnalyzer* scc_analyzer) : descriptor_(descriptor), classname_(ClassName(descriptor, false)), options_(options), field_generators_(descriptor, options), max_has_bit_index_(0), nested_generators_(new google::protobuf::scoped_ptr< - MessageGenerator>[descriptor->nested_type_count()]), + MessageGenerator>[descriptor->nested_type_count()]), enum_generators_( new google::protobuf::scoped_ptr<EnumGenerator>[descriptor->enum_type_count()]), extension_generators_(new google::protobuf::scoped_ptr< - ExtensionGenerator>[descriptor->extension_count()]), - use_dependent_base_(false) { - + ExtensionGenerator>[descriptor->extension_count()]), + use_dependent_base_(false), + num_weak_fields_(0), + scc_analyzer_(scc_analyzer) { // Compute optimized field order to be used for layout and initialization // purposes. for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->containing_oneof()) { - optimized_order_.push_back(descriptor_->field(i)); + const FieldDescriptor* field = descriptor_->field(i); + if (field->options().weak()) { + num_weak_fields_++; + } else if (!field->containing_oneof()) { + optimized_order_.push_back(field); } } OptimizePadding(&optimized_order_, options_); @@ -538,8 +564,8 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, } for (int i = 0; i < descriptor->nested_type_count(); i++) { - nested_generators_[i].reset( - new MessageGenerator(descriptor->nested_type(i), options)); + nested_generators_[i].reset(new MessageGenerator(descriptor->nested_type(i), + options, scc_analyzer)); } for (int i = 0; i < descriptor->enum_type_count(); i++) { @@ -565,6 +591,8 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, // Always make oneofs dependent. use_dependent_base_ = true; } + + table_driven_ = TableDrivenEnabled(descriptor_, options_); } MessageGenerator::~MessageGenerator() {} @@ -586,6 +614,7 @@ void MessageGenerator::Flatten(std::vector<MessageGenerator*>* list) { for (int i = 0; i < descriptor_->nested_type_count(); i++) { nested_generators_[i]->Flatten(list); } + index_in_file_messages_ = list->size(); list->push_back(this); } @@ -602,7 +631,6 @@ void MessageGenerator::AddGenerators( void MessageGenerator::FillMessageForwardDeclarations( std::map<string, const Descriptor*>* class_names) { - if (IsMapEntryMessage(descriptor_)) return; (*class_names)[classname_] = descriptor_; } @@ -644,7 +672,7 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) { ordered_fields.begin(), optimized_order_.begin(), optimized_order_.end()); for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); - if (field->containing_oneof() == NULL) { + if (field->containing_oneof() == NULL && !field->options().weak()) { continue; } ordered_fields.push_back(field); @@ -777,6 +805,15 @@ void MessageGenerator:: GenerateSingularFieldHasBits(const FieldDescriptor* field, std::map<string, string> vars, io::Printer* printer) { + if (field->options().weak()) { + printer->Print( + vars, + "$inline$" + "bool $classname$::has_$name$() const {\n" + " return _weak_field_map_.Has($number$);\n" + "}\n"); + return; + } if (HasFieldPresence(descriptor_->file())) { // N.B.: without field presence, we do not use has-bits or generate // has_$name$() methods. @@ -896,7 +933,7 @@ GenerateFieldClear(const FieldDescriptor* field, field_generators_.get(field) .GenerateClearingCode(printer); if (HasFieldPresence(descriptor_->file())) { - if (!field->is_repeated()) { + if (!field->is_repeated() && !field->options().weak()) { printer->Print(vars, "$this_message$clear_has_$name$();\n"); } @@ -1049,6 +1086,29 @@ GenerateClassDefinition(io::Printer* printer) { "}\n" "\n"); + // Generate move constructor and move assignment operator for types other than + // Any. + #ifdef PROTO_EXPERIMENTAL_ENABLE_MOVE + if (!IsAnyMessage(descriptor_)) { + printer->Print(vars, + "#if LANG_CXX11\n" + "$classname$($classname$&& from)\n" + " : $classname$() {\n" + " *this = ::std::move(from);\n" + "}\n" + "\n" + "inline $classname$& operator=($classname$&& from) {\n" + " if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {\n" + " InternalSwap(&from);\n" + " } else {\n" + " CopyFrom(from);\n" + " }\n" + " return *this;\n" + "}\n" + "#endif\n"); + } + #endif + if (PreserveUnknownFields(descriptor_)) { string type = UseUnknownFieldSet(descriptor_->file(), options_) ? "::google::protobuf::UnknownFieldSet" @@ -1119,12 +1179,15 @@ GenerateClassDefinition(io::Printer* printer) { } // TODO(gerbens) make this private, while still granting other protos access. + vars["message_index"] = SimpleItoa(index_in_file_messages_); printer->Print( vars, "static inline const $classname$* internal_default_instance() {\n" " return reinterpret_cast<const $classname$*>(\n" " &_$classname$_default_instance_);\n" "}\n" + "static PROTOBUF_CONSTEXPR int const kIndexInFileMessages =\n" + " $message_index$;\n" "\n"); @@ -1199,14 +1262,8 @@ GenerateClassDefinition(io::Printer* printer) { } if (HasFastArraySerialization(descriptor_->file(), options_)) { printer->Print( - "::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(\n" - " bool deterministic, ::google::protobuf::uint8* target) const PROTOBUF_FINAL;\n" - "::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output)\n" - " const PROTOBUF_FINAL {\n" - " return InternalSerializeWithCachedSizesToArray(\n" - " ::google::protobuf::io::CodedOutputStream::" - "IsDefaultSerializationDeterministic(), output);\n" - "}\n"); + "::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(\n" + " bool deterministic, ::google::protobuf::uint8* target) const PROTOBUF_FINAL;\n"); } } @@ -1312,7 +1369,8 @@ GenerateClassDefinition(io::Printer* printer) { for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->is_repeated()) { + if (!descriptor_->field(i)->is_repeated() && + !descriptor_->field(i)->options().weak()) { // set_has_***() generated in all proto1/2 code and in oneofs (only) for // messages without true field presence. if (HasFieldPresence(descriptor_->file()) || @@ -1451,6 +1509,10 @@ GenerateClassDefinition(io::Printer* printer) { "\n"); } + if (num_weak_fields_) { + printer->Print( + "::google::protobuf::internal::WeakFieldMap _weak_field_map_;\n"); + } // Generate _any_metadata_ for the Any type. if (IsAnyMessage(descriptor_)) { printer->Print(vars, @@ -1459,6 +1521,7 @@ GenerateClassDefinition(io::Printer* printer) { // The TableStruct struct needs access to the private parts, in order to // construct the offsets of all members. + // // Some InitDefault and Shutdown are defined as static member functions of // TableStruct such that they are also allowed to access private members. printer->Print( @@ -1511,8 +1574,9 @@ GenerateInlineMethods(io::Printer* printer, bool is_inline) { void MessageGenerator:: GenerateExtraDefaultFields(io::Printer* printer) { - // Generate oneof default instance for reflection usage. - if (descriptor_->oneof_decl_count() > 0) { + // Generate oneof default instance and weak field instances for reflection + // usage. + if (descriptor_->oneof_decl_count() > 0 || num_weak_fields_ > 0) { printer->Print("public:\n"); for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { @@ -1525,9 +1589,71 @@ GenerateExtraDefaultFields(io::Printer* printer) { field_generators_.get(field).GeneratePrivateMembers(printer); } } + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (field->options().weak()) { + printer->Print( + " const ::google::protobuf::Message* $name$_;\n", "name", FieldName(field)); + } + } } } +bool MessageGenerator::GenerateParseTable(io::Printer* printer, size_t offset, + size_t aux_offset) { + if (!table_driven_) { + printer->Print("{ NULL, NULL, 0, -1, -1, false },\n"); + return false; + } + + std::map<string, string> vars; + + vars["classname"] = classname_; + vars["offset"] = SimpleItoa(offset); + vars["aux_offset"] = SimpleItoa(aux_offset); + + int max_field_number = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (max_field_number < field->number()) { + max_field_number = field->number(); + } + } + + vars["max_field_number"] = SimpleItoa(max_field_number); + + printer->Print("{\n"); + printer->Indent(); + + printer->Print(vars, + "TableStruct::entries + $offset$,\n" + "TableStruct::aux + $aux_offset$,\n" + "$max_field_number$,\n"); + + if (!HasFieldPresence(descriptor_->file())) { + // If we don't have field presence, then _has_bits_ does not exist. + printer->Print(vars, "-1,\n"); + } else { + printer->Print(vars, + "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(\n" + " $classname$, _has_bits_),\n"); + } + + printer->Print(vars, + "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(\n" + " $classname$, _internal_metadata_),\n"); + + if (UseUnknownFieldSet(descriptor_->file(), options_)) { + printer->Print(vars, "true,\n"); + } else { + printer->Print(vars, "false,\n"); + } + + printer->Outdent(); + printer->Print("},\n"); + return true; +} + void MessageGenerator::GenerateSchema(io::Printer* printer, int offset, int has_offset) { if (IsMapEntryMessage(descriptor_)) return; @@ -1545,46 +1671,7 @@ void MessageGenerator::GenerateSchema(io::Printer* printer, int offset, } void MessageGenerator:: -GenerateTypeRegistrations(io::Printer* printer) { - // Register this message type with the message factory. - if (IsMapEntryMessage(descriptor_)) { - std::map<string, string> vars; - CollectMapInfo(descriptor_, &vars); - vars["classname"] = classname_; - vars["file_namespace"] = FileLevelNamespace(descriptor_->file()->name()); - - const FieldDescriptor* val = descriptor_->FindFieldByName("value"); - if (descriptor_->file()->syntax() == FileDescriptor::SYNTAX_PROTO2 && - val->type() == FieldDescriptor::TYPE_ENUM) { - const EnumValueDescriptor* default_value = val->default_value_enum(); - vars["default_enum_value"] = Int32ToString(default_value->number()); - } else { - vars["default_enum_value"] = "0"; - } - - vars["index_in_metadata"] = SimpleItoa(index_in_metadata_); - - printer->Print( - vars, - "const ::google::protobuf::Descriptor* $classname$_descriptor = " - "$file_namespace$::file_level_metadata[$index_in_metadata$].descriptor;" - "\n" - "::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(\n" - " $classname$_descriptor,\n" - " ::google::protobuf::internal::MapEntry<\n" - " $key$,\n" - " $val$,\n" - " $key_wire_type$,\n" - " $val_wire_type$,\n" - " $default_enum_value$>::CreateDefaultInstance(\n" - " $classname$_descriptor));\n"); - } -} - -void MessageGenerator:: GenerateDefaultInstanceAllocator(io::Printer* printer) { - if (IsMapEntryMessage(descriptor_)) return; - // Construct the default instances of all fields, as they will be used // when creating the default instance of the entire message. for (int i = 0; i < descriptor_->field_count(); i++) { @@ -1601,7 +1688,15 @@ GenerateDefaultInstanceAllocator(io::Printer* printer) { void MessageGenerator:: GenerateDefaultInstanceInitializer(io::Printer* printer) { - if (IsMapEntryMessage(descriptor_)) return; + if (IsMapEntryMessage(descriptor_)) { + printer->Print( + "_$classname$_default_instance_.get_mutable()->set_default_instance(_$" + "classname$_default_instance_.get_mutable());\n" + "_$classname$_default_instance_.get_mutable()->InitAsDefaultInstance();" + "\n", + "classname", classname_); + return; + } // The default instance needs all of its embedded message pointers // cross-linked to other default instances. We can't do this initialization @@ -1617,7 +1712,7 @@ GenerateDefaultInstanceInitializer(io::Printer* printer) { (field->containing_oneof() == NULL || HasDescriptorMethods(descriptor_->file(), options_))) { string name; - if (field->containing_oneof()) { + if (field->containing_oneof() || field->options().weak()) { name = "_" + classname_ + "_default_instance_."; } else { name = "_" + classname_ + "_default_instance_.get_mutable()->"; @@ -1645,7 +1740,7 @@ GenerateShutdownCode(io::Printer* printer) { if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print("delete file_level_metadata[$index$].reflection;\n", "index", - SimpleItoa(index_in_metadata_)); + SimpleItoa(index_in_file_messages_)); } // Handle default instances of fields. @@ -1657,7 +1752,31 @@ GenerateShutdownCode(io::Printer* printer) { void MessageGenerator:: GenerateClassMethods(io::Printer* printer) { - if (IsMapEntryMessage(descriptor_)) return; + if (IsMapEntryMessage(descriptor_)) { + if (HasDescriptorMethods(descriptor_->file(), options_)) { + printer->Print( + "$parent$::$classname$::$classname$() {}\n" + "$parent$::$classname$::$classname$(::google::protobuf::Arena* arena) : " + "SuperType(arena) {}\n" + "::google::protobuf::Metadata $parent$::$classname$::GetMetadata() const {\n" + " $file_namespace$::protobuf_AssignDescriptorsOnce();\n" + " return $file_namespace$::file_level_metadata[$index$];\n" + "}\n" + "void $parent$::$classname$::MergeFrom(\n" + " const ::google::protobuf::Message& other) {\n" + " ::google::protobuf::Message::MergeFrom(other);\n" + "}\n" + "void $parent$::$classname$::MergeFrom(const $classname$& other) {\n" + " MergeFromInternal(other);\n" + "}\n" + "\n", + "file_namespace", FileLevelNamespace(descriptor_->file()->name()), + "parent", ClassName(descriptor_->containing_type(), false), + "classname", classname_, "index", + SimpleItoa(index_in_file_messages_)); + } + return; + } if (IsAnyMessage(descriptor_)) { printer->Print( @@ -1739,11 +1858,12 @@ GenerateClassMethods(io::Printer* printer) { printer->Print( "::google::protobuf::Metadata $classname$::GetMetadata() const {\n" " $file_namespace$::protobuf_AssignDescriptorsOnce();\n" - " return $file_namespace$::file_level_metadata[$index$];\n" + " return " + "$file_namespace$::file_level_metadata[kIndexInFileMessages];\n" "}\n" "\n", - "classname", classname_, "index", SimpleItoa(index_in_metadata_), - "file_namespace", FileLevelNamespace(descriptor_->file()->name())); + "classname", classname_, "file_namespace", + FileLevelNamespace(descriptor_->file()->name())); } else { printer->Print( "::std::string $classname$::GetTypeName() const {\n" @@ -1756,6 +1876,185 @@ GenerateClassMethods(io::Printer* printer) { } +size_t MessageGenerator::GenerateParseOffsets(io::Printer* printer) { + if (!table_driven_) { + return 0; + } + + // Field "0" is special: We use it in our switch statement of processing + // types to handle the successful end tag case. + printer->Print("{0, 0, 0, ::google::protobuf::internal::kInvalidMask, 0, 0},\n"); + int last_field_number = 1; + + std::vector<const FieldDescriptor*> ordered_fields = + SortFieldsByNumber(descriptor_); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = ordered_fields[i]; + GOOGLE_CHECK_GE(field->number(), last_field_number); + + for (; last_field_number < field->number(); last_field_number++) { + printer->Print( + "{ 0, 0, ::google::protobuf::internal::kInvalidMask,\n" + " ::google::protobuf::internal::kInvalidMask, 0, 0 },\n"); + } + last_field_number++; + + unsigned char normal_wiretype, packed_wiretype, processing_type; + normal_wiretype = WireFormat::WireTypeForFieldType(field->type()); + + if (field->is_packable()) { + packed_wiretype = WireFormatLite::WIRETYPE_LENGTH_DELIMITED; + } else { + packed_wiretype = internal::kNotPackedMask; + } + + processing_type = static_cast<unsigned>(field->type()); + if (field->type() == FieldDescriptor::TYPE_STRING) { + switch (EffectiveStringCType(field)) { + case FieldOptions::STRING: + default: + break; + } + } else if (field->type() == FieldDescriptor::TYPE_BYTES) { + switch (EffectiveStringCType(field)) { + case FieldOptions::STRING: + default: + break; + } + } + + processing_type |= static_cast<unsigned>( + field->is_repeated() ? internal::kRepeatedMask : 0); + const unsigned char tag_size = + WireFormat::TagSize(field->number(), field->type()); + + std::map<string, string> vars; + vars["classname"] = classname_; + vars["name"] = FieldName(field); + vars["has"] = SimpleItoa(has_bit_indices_[field->index()]); + vars["nwtype"] = SimpleItoa(normal_wiretype); + vars["pwtype"] = SimpleItoa(packed_wiretype); + vars["ptype"] = SimpleItoa(processing_type); + vars["tag_size"] = SimpleItoa(tag_size); + + printer->Print(vars, + "{\n" + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(\n" + " $classname$, $name$_),\n" + " static_cast< ::google::protobuf::uint32>($has$),\n" + " $nwtype$, $pwtype$, $ptype$, $tag_size$\n" + "},\n"); + } + + return last_field_number; +} + +size_t MessageGenerator::GenerateParseAuxTable(io::Printer* printer) { + if (!table_driven_) { + return 0; + } + + std::vector<const FieldDescriptor*> ordered_fields = + SortFieldsByNumber(descriptor_); + + printer->Print("::google::protobuf::internal::AuxillaryParseTableField(),\n"); + int last_field_number = 1; + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = ordered_fields[i]; + + GOOGLE_CHECK_GE(field->number(), last_field_number); + for (; last_field_number < field->number(); last_field_number++) { + printer->Print("::google::protobuf::internal::AuxillaryParseTableField(),\n"); + } + + std::map<string, string> vars; + SetCommonFieldVariables(field, &vars, options_); + + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_ENUM: + vars["type"] = ClassName(field->enum_type(), true); + printer->Print( + vars, + "{::google::protobuf::internal::AuxillaryParseTableField::enum_aux{" + "$type$_IsValid, \"$type$\" }},\n"); + last_field_number++; + break; + case FieldDescriptor::CPPTYPE_MESSAGE: { + std::vector<string> package_parts; + + const Descriptor* outer = field->message_type(); + while (outer->containing_type() != NULL) { + outer = outer->containing_type(); + } + + package_parts = Split( + outer->full_name(), ".", true); + // outer->full_name() contains the class itself. Remove it as it is + // used in the name of the default instance variable. + GOOGLE_DCHECK_NE(package_parts.size(), 0); + package_parts.back().clear(); + + vars["classname"] = ClassName(field->message_type(), false); + vars["ns"] = Join(package_parts, "::"); + vars["type"] = FieldMessageTypeName(field); + vars["file_namespace"] = FileLevelNamespace(outer->file()->name()); + + printer->Print(vars, + "{::google::protobuf::internal::AuxillaryParseTableField::message_aux{\n" + " &::$ns$_$classname$_default_instance_,\n"); + + bool dont_emit_table = + !TableDrivenEnabled(field->message_type(), options_); + + if (dont_emit_table) { + printer->Print(" NULL,\n"); + } else { + printer->Print(vars, + " ::$ns$$file_namespace$::TableStruct::schema +\n" + " ::$ns$$classname$::kIndexInFileMessages,\n"); + } + + printer->Print("}},\n"); + last_field_number++; + break; + } + case FieldDescriptor::CPPTYPE_STRING: + switch (EffectiveStringCType(field)) { + case FieldOptions::STRING: + vars["default"] = + field->default_value_string().empty() + ? "&::google::protobuf::internal::fixed_address_empty_string" + : "&" + classname_ + "::_default_" + FieldName(field) + "_"; + break; + case FieldOptions::CORD: + case FieldOptions::STRING_PIECE: + vars["default"] = + "\"" + CEscape(field->default_value_string()) + "\""; + break; + } + vars["full_name"] = field->full_name(); + vars["strict"] = + field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 + ? "true" : "false"; + vars["type"] = field->full_name(); + printer->Print(vars, + "{::google::protobuf::internal::AuxillaryParseTableField::string_aux{\n" + " $default$,\n" + " \"$full_name$\",\n" + " $strict$,\n" + " \"$type$\"\n" + "}},\n"); + last_field_number++; + break; + default: + break; + } + } + + return last_field_number; +} + std::pair<size_t, size_t> MessageGenerator::GenerateOffsets( io::Printer* printer) { if (IsMapEntryMessage(descriptor_)) return std::make_pair(0, 0); @@ -1765,7 +2064,8 @@ std::pair<size_t, size_t> MessageGenerator::GenerateOffsets( if (HasFieldPresence(descriptor_->file())) { printer->Print( variables, - "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, _has_bits_),\n"); + "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, " + "_has_bits_),\n"); } else { printer->Print("~0u, // no _has_bits_\n"); } @@ -1775,34 +2075,41 @@ std::pair<size_t, size_t> MessageGenerator::GenerateOffsets( if (descriptor_->extension_range_count() > 0) { printer->Print( variables, - "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, _extensions_),\n"); + "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, " + "_extensions_),\n"); } else { printer->Print("~0u, // no _extensions_\n"); } if (descriptor_->oneof_decl_count() > 0) { printer->Print(variables, - "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, " - "_oneof_case_[0]),\n"); + "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" + "$classname$, _oneof_case_[0]),\n"); } else { printer->Print("~0u, // no _oneof_case_\n"); } - - const int kNumGenericOffsets = 4; // the number of fixed offsets above + if (num_weak_fields_ > 0) { + printer->Print(variables, + "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$," + " _weak_field_map_),\n"); + } else { + printer->Print("~0u, // no _weak_field_map_\n"); + } + const int kNumGenericOffsets = 5; // the number of fixed offsets above const size_t offsets = kNumGenericOffsets + descriptor_->field_count() + descriptor_->oneof_decl_count(); size_t entries = offsets; for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); - if (field->containing_oneof()) { + if (field->containing_oneof() || field->options().weak()) { printer->Print( - "PROTO2_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(" + "GOOGLE_PROTOBUF_GENERATED_DEFAULT_ONEOF_FIELD_OFFSET(" "(&_$classname$_default_instance_), $name$_),\n", "classname", classname_, "name", FieldName(field)); } else { printer->Print( "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, " - "$name$_),\n", + "$name$_),\n", "classname", classname_, "name", FieldName(field)); } @@ -1844,19 +2151,6 @@ GenerateSharedConstructorCode(io::Printer* printer) { need_to_clear_cached_size = false; } - // TODO(gerbens) Clean this hack, and why do i need a reference to a pointer?? - for (int i = 0; i < descriptor_->nested_type_count(); i++) { - if (HasDescriptorMethods(descriptor_->file(), options_) && - IsMapEntryMessage(descriptor_->nested_type(i))) { - printer->Print( - "const ::google::protobuf::Descriptor*& $type$_descriptor = " - "$file_namespace$::file_level_metadata[$index$].descriptor;\n", - "type", ClassName(descriptor_->nested_type(i), false), "index", - SimpleItoa(nested_generators_[i]->index_in_metadata_), - "file_namespace", FileLevelNamespace(descriptor_->file()->name())); - } - } - std::vector<bool> processed(optimized_order_.size(), false); GenerateConstructorBody(printer, processed, false); @@ -1906,6 +2200,9 @@ GenerateSharedDestructorCode(io::Printer* printer) { "oneof_name", descriptor_->oneof_decl(i)->name()); } + if (num_weak_fields_) { + printer->Print("_weak_field_map_.ClearAll();\n"); + } printer->Outdent(); printer->Print( "}\n" @@ -1956,6 +2253,12 @@ GenerateArenaDestructorCode(io::Printer* printer) { } } } + if (num_weak_fields_) { + // _this is the object being destructed (we are inside a static method + // here). + printer->Print("_this->_weak_field_map_.ClearAll();\n"); + need_registration = true; + } printer->Outdent(); printer->Print( @@ -2077,12 +2380,18 @@ GenerateStructors(io::Printer* printer) { if (IsAnyMessage(descriptor_)) { initializer_with_arena += ",\n _any_metadata_(&type_url_, &value_)"; } + if (num_weak_fields_ > 0) { + initializer_with_arena += ", _weak_field_map_(arena)"; + } string initializer_null; initializer_null = ", _internal_metadata_(NULL)"; if (IsAnyMessage(descriptor_)) { initializer_null += ", _any_metadata_(&type_url_, &value_)"; } + if (num_weak_fields_ > 0) { + initializer_null += ", _weak_field_map_(NULL)"; + } printer->Print( "$classname$::$classname$()\n" @@ -2165,6 +2474,9 @@ GenerateStructors(io::Printer* printer) { if (IsAnyMessage(descriptor_)) { printer->Print(",\n_any_metadata_(&type_url_, &value_)"); } + if (num_weak_fields_ > 0) { + printer->Print(",\n_weak_field_map_(from._weak_field_map_)"); + } printer->Outdent(); printer->Outdent(); @@ -2177,19 +2489,6 @@ GenerateStructors(io::Printer* printer) { printer->Print("_extensions_.MergeFrom(from._extensions_);\n"); } - // TODO(gerbens) Clean this hack, and why do i need a reference to a pointer?? - for (int i = 0; i < descriptor_->nested_type_count(); i++) { - if (HasDescriptorMethods(descriptor_->file(), options_) && - IsMapEntryMessage(descriptor_->nested_type(i))) { - printer->Print( - "const ::google::protobuf::Descriptor*& $type$_descriptor = " - "$file_namespace$::file_level_metadata[$index$].descriptor;\n", - "type", ClassName(descriptor_->nested_type(i), false), "index", - SimpleItoa(nested_generators_[i]->index_in_metadata_), - "file_namespace", FileLevelNamespace(descriptor_->file()->name())); - } - } - GenerateConstructorBody(printer, processed, true); // Copy oneof fields. Oneof field requires oneof case check. @@ -2268,11 +2567,13 @@ GenerateStructors(io::Printer* printer) { printer->Print( "const ::google::protobuf::Descriptor* $classname$::descriptor() {\n" " $file_namespace$::protobuf_AssignDescriptorsOnce();\n" - " return $file_namespace$::file_level_metadata[$index$].descriptor;\n" + " return " + "$file_namespace$::file_level_metadata[kIndexInFileMessages]." + "descriptor;\n" "}\n" "\n", - "index", SimpleItoa(index_in_metadata_), "classname", classname_, - "file_namespace", FileLevelNamespace(descriptor_->file()->name())); + "classname", classname_, "file_namespace", + FileLevelNamespace(descriptor_->file()->name())); } printer->Print( @@ -2504,6 +2805,10 @@ GenerateClear(io::Printer* printer) { "oneof_name", descriptor_->oneof_decl(i)->name()); } + if (num_weak_fields_) { + printer->Print("_weak_field_map_.ClearAll();\n"); + } + if (HasFieldPresence(descriptor_->file())) { // Step 5: Everything else. printer->Print("_has_bits_.Clear();\n"); @@ -2647,6 +2952,10 @@ GenerateSwap(io::Printer* printer) { if (descriptor_->extension_range_count() > 0) { printer->Print("_extensions_.Swap(&other->_extensions_);\n"); } + if (num_weak_fields_) { + printer->Print( + "_weak_field_map_.UnsafeArenaSwap(&other->_weak_field_map_);\n"); + } } else { printer->Print("GetReflection()->Swap(this, other);"); } @@ -2714,7 +3023,14 @@ GenerateMergeFrom(io::Printer* printer) { } printer->Print( - "_internal_metadata_.MergeFrom(from._internal_metadata_);\n"); + "_internal_metadata_.MergeFrom(from._internal_metadata_);\n" + "::google::protobuf::uint32 cached_has_bits = 0;\n" + "(void) cached_has_bits;\n\n"); + + // cached_has_bit_index maintains that: + // cached_has_bits = from._has_bits_[cached_has_bit_index] + // for cached_has_bit_index >= 0 + int cached_has_bit_index = -1; int last_i = -1; for (int i = 0; i < optimized_order_.size(); ) { @@ -2734,7 +3050,7 @@ GenerateMergeFrom(io::Printer* printer) { generator.GenerateMergingCode(printer); } - // Merge Optional and Required fields (after a _has_bit check). + // Merge Optional and Required fields (after a _has_bit_ check). int last_chunk = -1; int last_chunk_start = -1; int last_chunk_end = -1; @@ -2779,23 +3095,43 @@ GenerateMergeFrom(io::Printer* printer) { GOOGLE_DCHECK_LE(2, count); GOOGLE_DCHECK_GE(8, count); + if (cached_has_bit_index != last_chunk / 4) { + int new_index = last_chunk / 4; + printer->Print("cached_has_bits = from._has_bits_[$new_index$];\n", + "new_index", SimpleItoa(new_index)); + cached_has_bit_index = new_index; + } + printer->Print( - "if (from._has_bits_[$index$ / 32] & $mask$u) {\n", - "index", SimpleItoa(last_chunk * 8), + "if (cached_has_bits & $mask$u) {\n", "mask", SimpleItoa(last_chunk_mask)); printer->Indent(); } // Go back and emit clears for each of the fields we processed. + bool deferred_has_bit_changes = false; for (int j = last_chunk_start; j <= last_chunk_end; j++) { const FieldDescriptor* field = optimized_order_[j]; const FieldGenerator& generator = field_generators_.get(field); bool have_enclosing_if = false; if (HasFieldPresence(descriptor_->file())) { - printer->Print( - "if (from.has_$name$()) {\n", - "name", FieldName(field)); + // Attempt to use the state of cached_has_bits, if possible. + int has_bit_index = has_bit_indices_[field->index()]; + if (!field->options().weak() && + cached_has_bit_index == has_bit_index / 32) { + const string mask = StrCat( + strings::Hex(1u << (has_bit_index % 32), + strings::ZERO_PAD_8)); + + printer->Print( + "if (cached_has_bits & 0x$mask$u) {\n", "mask", mask); + } else { + printer->Print( + "if (from.has_$name$()) {\n", + "name", FieldName(field)); + } + printer->Indent(); have_enclosing_if = true; } else { @@ -2805,7 +3141,17 @@ GenerateMergeFrom(io::Printer* printer) { printer, "from.", field); } - generator.GenerateMergingCode(printer); + if (have_outer_if && IsPOD(field)) { + // GenerateCopyConstructorCode for enum and primitive scalar fields + // does not do _has_bits_ modifications. We defer _has_bits_ + // manipulation until the end of the outer if. + // + // This can reduce the number of loads/stores by up to 7 per 8 fields. + deferred_has_bit_changes = true; + generator.GenerateCopyConstructorCode(printer); + } else { + generator.GenerateMergingCode(printer); + } if (have_enclosing_if) { printer->Outdent(); @@ -2814,6 +3160,14 @@ GenerateMergeFrom(io::Printer* printer) { } if (have_outer_if) { + if (deferred_has_bit_changes) { + // Flush the has bits for the primitives we deferred. + GOOGLE_CHECK_LE(0, cached_has_bit_index); + printer->Print( + "_has_bits_[$index$] |= cached_has_bits;\n", + "index", SimpleItoa(cached_has_bit_index)); + } + printer->Outdent(); printer->Print("}\n"); } @@ -2849,6 +3203,9 @@ GenerateMergeFrom(io::Printer* printer) { printer->Print( "}\n"); } + if (num_weak_fields_) { + printer->Print("_weak_field_map_.MergeFrom(from._weak_field_map_);\n"); + } printer->Outdent(); printer->Print("}\n"); @@ -2913,13 +3270,36 @@ GenerateMergeFromCodedStream(io::Printer* printer) { return; } + std::vector<const FieldDescriptor*> ordered_fields = + SortFieldsByNumber(descriptor_); + printer->Print( "bool $classname$::MergePartialFromCodedStream(\n" - " ::google::protobuf::io::CodedInputStream* input) {\n" - "#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure\n" - " ::google::protobuf::uint32 tag;\n", + " ::google::protobuf::io::CodedInputStream* input) {\n", "classname", classname_); + if (table_driven_) { + printer->Indent(); + + printer->Print( + "return ::google::protobuf::internal::MergePartialFromCodedStream(\n" + " this,\n" + " $file_namespace$::TableStruct::schema[\n" + " $classname$::kIndexInFileMessages],\n" + " input);\n", + "classname", classname_, + "file_namespace", FileLevelNamespace(descriptor_->file()->name())); + + printer->Outdent(); + + printer->Print("}\n"); + return; + } + + printer->Print( + "#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure\n" + " ::google::protobuf::uint32 tag;\n"); + if (PreserveUnknownFields(descriptor_) && !UseUnknownFieldSet(descriptor_->file(), options_)) { // Use LazyStringOutputString to avoid initializing unknown fields string @@ -2928,7 +3308,8 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Print( " ::google::protobuf::io::LazyStringOutputStream unknown_fields_string(\n" " ::google::protobuf::NewPermanentCallback(&_internal_metadata_,\n" - " &::google::protobuf::internal::InternalMetadataWithArenaLite::mutable_unknown_fields));\n" + " &::google::protobuf::internal::InternalMetadataWithArenaLite::\n" + " mutable_unknown_fields));\n" " ::google::protobuf::io::CodedOutputStream unknown_fields_stream(\n" " &unknown_fields_string, false);\n", "classname", classname_); @@ -2942,8 +3323,6 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Print("for (;;) {\n"); printer->Indent(); - std::vector<const FieldDescriptor*> ordered_fields = - SortFieldsByNumber(descriptor_); uint32 maxtag = descriptor_->field_count() == 0 ? 0 : WireFormat::MakeTag(ordered_fields[descriptor_->field_count() - 1]); const int kCutoff0 = 127; // fits in 1-byte varint @@ -3004,20 +3383,8 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Indent(); - // Find repeated messages and groups now, to simplify what follows. - hash_set<int> fields_with_parse_loop; - for (int i = 0; i < ordered_fields.size(); i++) { - const FieldDescriptor* field = ordered_fields[i]; - if (field->is_repeated() && - (field->type() == FieldDescriptor::TYPE_MESSAGE || - field->type() == FieldDescriptor::TYPE_GROUP)) { - fields_with_parse_loop.insert(i); - } - } - for (int i = 0; i < ordered_fields.size(); i++) { const FieldDescriptor* field = ordered_fields[i]; - const bool loops = fields_with_parse_loop.count(i) > 0; PrintFieldComment(printer, field); @@ -3030,11 +3397,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) { // Emit code to parse the common, expected case. printer->Print("if (static_cast< ::google::protobuf::uint8>(tag) ==\n" " static_cast< ::google::protobuf::uint8>($commontag$u)) {\n", - "commontag", SimpleItoa(WireFormat::MakeTag(field))); - - if (loops) { - printer->Print(" DO_(input->IncrementRecursionDepth());\n"); - } + "commontag", SimpleItoa(WireFormat::MakeTag(field))); printer->Indent(); if (field->is_packed()) { @@ -3074,12 +3437,6 @@ GenerateMergeFromCodedStream(io::Printer* printer) { " goto handle_unusual;\n" "}\n"); - // For repeated messages/groups, we need to decrement recursion depth. - if (loops) { - printer->Print( - "input->UnsafeDecrementRecursionDepth();\n"); - } - printer->Print( "break;\n"); @@ -3194,7 +3551,7 @@ void MessageGenerator::GenerateSerializeOneofFields( bool to_array) { GOOGLE_CHECK(!fields.empty()); if (fields.size() == 1) { - GenerateSerializeOneField(printer, fields[0], to_array); + GenerateSerializeOneField(printer, fields[0], to_array, -1); return; } // We have multiple mutually exclusive choices. Emit a switch statement. @@ -3227,14 +3584,31 @@ void MessageGenerator::GenerateSerializeOneofFields( } void MessageGenerator::GenerateSerializeOneField( - io::Printer* printer, const FieldDescriptor* field, bool to_array) { - PrintFieldComment(printer, field); + io::Printer* printer, const FieldDescriptor* field, bool to_array, + int cached_has_bits_index) { + if (!field->options().weak()) { + // For weakfields, PrintFieldComment is called during iteration. + PrintFieldComment(printer, field); + } bool have_enclosing_if = false; - if (!field->is_repeated() && HasFieldPresence(descriptor_->file())) { - printer->Print( - "if (has_$name$()) {\n", - "name", FieldName(field)); + if (field->options().weak()) { + } else if (!field->is_repeated() && HasFieldPresence(descriptor_->file())) { + // Attempt to use the state of cached_has_bits, if possible. + int has_bit_index = has_bit_indices_[field->index()]; + if (cached_has_bits_index == has_bit_index / 32) { + const string mask = StrCat( + strings::Hex(1u << (has_bit_index % 32), + strings::ZERO_PAD_8)); + + printer->Print( + "if (cached_has_bits & 0x$mask$u) {\n", "mask", mask); + } else { + printer->Print( + "if (has_$name$()) {\n", + "name", FieldName(field)); + } + printer->Indent(); have_enclosing_if = true; } else if (!HasFieldPresence(descriptor_->file())) { @@ -3341,7 +3715,6 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) { "classname", classname_); printer->Indent(); - printer->Print("(void)deterministic; // Unused\n"); printer->Print( "// @@protoc_insertion_point(serialize_to_array_start:$full_name$)\n", "full_name", descriptor_->full_name()); @@ -3372,7 +3745,8 @@ GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) { : mg_(mg), printer_(printer), to_array_(to_array), - eager_(!HasFieldPresence(mg->descriptor_->file())) {} + eager_(!HasFieldPresence(mg->descriptor_->file())), + cached_has_bit_index_(-1) {} ~LazySerializerEmitter() { Flush(); } @@ -3383,7 +3757,27 @@ GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) { Flush(); } if (field->containing_oneof() == NULL) { - mg_->GenerateSerializeOneField(printer_, field, to_array_); + // TODO(ckennelly): Defer non-oneof fields similarly to oneof fields. + + if (!field->options().weak() && !field->is_repeated() && !eager_) { + // We speculatively load the entire _has_bits_[index] contents, even + // if it is for only one field. Deferring non-oneof emitting would + // allow us to determine whether this is going to be useful. + int has_bit_index = mg_->has_bit_indices_[field->index()]; + if (cached_has_bit_index_ != has_bit_index / 32) { + // Reload. + int new_index = has_bit_index / 32; + + printer_->Print( + "cached_has_bits = _has_bits_[$new_index$];\n", + "new_index", SimpleItoa(new_index)); + + cached_has_bit_index_ = new_index; + } + } + + mg_->GenerateSerializeOneField( + printer_, field, to_array_, cached_has_bit_index_); } else { v_.push_back(field); } @@ -3409,6 +3803,11 @@ GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) { const bool to_array_; const bool eager_; std::vector<const FieldDescriptor*> v_; + + // cached_has_bit_index_ maintains that: + // cached_has_bits = from._has_bits_[cached_has_bit_index_] + // for cached_has_bit_index_ >= 0 + int cached_has_bit_index_; }; std::vector<const FieldDescriptor*> ordered_fields = @@ -3420,29 +3819,51 @@ GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) { } std::sort(sorted_extensions.begin(), sorted_extensions.end(), ExtensionRangeSorter()); + if (num_weak_fields_) { + printer->Print( + "::google::protobuf::internal::WeakFieldMap::FieldWriter field_writer(" + "_weak_field_map_);\n"); + } + + printer->Print( + "::google::protobuf::uint32 cached_has_bits = 0;\n" + "(void) cached_has_bits;\n\n"); // Merge the fields and the extension ranges, both sorted by field number. { LazySerializerEmitter e(this, printer, to_array); + const FieldDescriptor* last_weak_field = NULL; int i, j; for (i = 0, j = 0; i < ordered_fields.size() || j < sorted_extensions.size();) { - if (i == descriptor_->field_count()) { - e.Flush(); - GenerateSerializeOneExtensionRange(printer, - sorted_extensions[j++], - to_array); - } else if (j == sorted_extensions.size()) { - e.Emit(ordered_fields[i++]); - } else if (ordered_fields[i]->number() < sorted_extensions[j]->start) { - e.Emit(ordered_fields[i++]); - } else { + if ((j == sorted_extensions.size()) || + (i < descriptor_->field_count() && + ordered_fields[i]->number() < sorted_extensions[j]->start)) { + const FieldDescriptor* field = ordered_fields[i++]; + if (field->options().weak()) { + last_weak_field = field; + PrintFieldComment(printer, field); + } else { + if (last_weak_field != NULL) { + e.Emit(last_weak_field); + last_weak_field = NULL; + } + e.Emit(field); + } + } else { + if (last_weak_field != NULL) { + e.Emit(last_weak_field); + last_weak_field = NULL; + } e.Flush(); GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++], to_array); } } + if (last_weak_field != NULL) { + e.Emit(last_weak_field); + } } if (PreserveUnknownFields(descriptor_)) { @@ -3783,6 +4204,11 @@ GenerateByteSize(io::Printer* printer) { "}\n"); } + if (num_weak_fields_) { + // TagSize + MessageSize + printer->Print("total_size += _weak_field_map_.ByteSizeLong();\n"); + } + // We update _cached_size_ even though this is a const method. In theory, // this is not thread-compatible, because concurrent writes have undefined // results. In practice, since any concurrent writes will be writing the @@ -3840,26 +4266,28 @@ GenerateIsInitialized(io::Printer* printer) { // TODO(ckennelly): Push this down into a generator? if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && !ShouldIgnoreRequiredFieldCheck(field, options_) && - HasRequiredFields(field->message_type(), options_)) { + scc_analyzer_->HasRequiredFields(field->message_type())) { if (field->is_repeated()) { printer->Print( "if (!::google::protobuf::internal::AllAreInitialized(this->$name$()))" " return false;\n", "name", FieldName(field)); + } else if (field->options().weak()) { + continue; } else { - GOOGLE_CHECK(field->options().weak() || !field->containing_oneof()); - // For weak fields, use the data member (::google::protobuf::Message*) instead - // of the getter to avoid a link dependency on the weak message type - // which is only forward declared. + GOOGLE_CHECK(!field->containing_oneof()); printer->Print( "if (has_$name$()) {\n" " if (!this->$name$_->IsInitialized()) return false;\n" "}\n", - "name", FieldName(field)); + "name", FieldName(field)); } } } - + if (num_weak_fields_) { + // For Weak fields. + printer->Print("if (!_weak_field_map_.IsInitialized()) return false;\n"); + } // Go through the oneof fields, emitting a switch if any might have required // fields. for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { @@ -3871,7 +4299,7 @@ GenerateIsInitialized(io::Printer* printer) { if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && !ShouldIgnoreRequiredFieldCheck(field, options_) && - HasRequiredFields(field->message_type(), options_)) { + scc_analyzer_->HasRequiredFields(field->message_type())) { has_required_fields = true; break; } @@ -3894,17 +4322,10 @@ GenerateIsInitialized(io::Printer* printer) { if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && !ShouldIgnoreRequiredFieldCheck(field, options_) && - HasRequiredFields(field->message_type(), options_)) { + scc_analyzer_->HasRequiredFields(field->message_type())) { GOOGLE_CHECK(!(field->options().weak() || !field->containing_oneof())); if (field->options().weak()) { - // For weak fields, use the data member (::google::protobuf::Message*) instead - // of the getter to avoid a link dependency on the weak message type - // which is only forward declared. - printer->Print( - "if (has_$name$()) {\n" - " if (!this->$name$_->IsInitialized()) return false;\n" - "}\n", - "name", FieldName(field)); + // Just skip. } else { printer->Print( "if (has_$name$()) {\n" |