diff options
Diffstat (limited to 'src/google/protobuf/compiler/cpp/cpp_message.cc')
-rw-r--r-- | src/google/protobuf/compiler/cpp/cpp_message.cc | 855 |
1 files changed, 527 insertions, 328 deletions
diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index 1f1a0f1c..f5648663 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -85,14 +85,13 @@ struct FieldOrderingByNumber { // Sort the fields of the given Descriptor by number into a new[]'d array // and return it. -const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) { - const FieldDescriptor** fields = - new const FieldDescriptor*[descriptor->field_count()]; +std::vector<const FieldDescriptor*> SortFieldsByNumber( + const Descriptor* descriptor) { + std::vector<const FieldDescriptor*> fields(descriptor->field_count()); for (int i = 0; i < descriptor->field_count(); i++) { fields[i] = descriptor->field(i); } - std::sort(fields, fields + descriptor->field_count(), - FieldOrderingByNumber()); + std::sort(fields.begin(), fields.end(), FieldOrderingByNumber()); return fields; } @@ -227,59 +226,150 @@ class FieldGroup { // used in a vector. }; +// Helper for the code that emits the Clear() method. +bool CanInitializeByZeroing(const FieldDescriptor* field) { + if (field->is_repeated() || field->is_extension()) return false; + switch (field->cpp_type()) { + case internal::WireFormatLite::CPPTYPE_ENUM: + return field->default_value_enum()->number() == 0; + case internal::WireFormatLite::CPPTYPE_INT32: + return field->default_value_int32() == 0; + case internal::WireFormatLite::CPPTYPE_INT64: + return field->default_value_int64() == 0; + case internal::WireFormatLite::CPPTYPE_UINT32: + return field->default_value_uint32() == 0; + case internal::WireFormatLite::CPPTYPE_UINT64: + return field->default_value_uint64() == 0; + case internal::WireFormatLite::CPPTYPE_FLOAT: + return field->default_value_float() == 0; + case internal::WireFormatLite::CPPTYPE_DOUBLE: + return field->default_value_double() == 0; + case internal::WireFormatLite::CPPTYPE_BOOL: + return field->default_value_bool() == false; + default: + return false; + } +} + // Reorder 'fields' so that if the fields are output into a c++ class in the new -// order, the alignment padding is minimized. We try to do this while keeping -// each field as close as possible to its original position so that we don't -// reduce cache locality much for function that access each field in order. +// 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. +// +// TODO(ckennelly): Update these functions to use the optimized layout order +// for their access patterns. +// +// TODO(ckennelly): If/when we have profiles available for the compiler, use +// those rather than respect declaration order. +// +// We classify each field into a particular "family" of fields, that we perform +// the same operation on in our generated functions. +// +// REPEATED is placed first, as the C++ compiler automatically initializes +// these fields in layout order. +// +// STRING is grouped next, as our Clear/SharedCtor/SharedDtor walks it and +// calls ArenaStringPtr::Destroy on each. +// +// MESSAGE is grouped next, as our Clear/SharedDtor code walks it and calls +// delete on each. We initialize these fields with a NULL pointer (see +// MessageFieldGenerator::GenerateConstructorCode). +// TODO(ckennelly): memset these in SharedCtor. +// +// ZERO_INITIALIZABLE is memset in Clear/SharedCtor +// +// OTHER these fields are initialized one-by-one. void OptimizePadding(vector<const FieldDescriptor*>* fields) { + // The sorted numeric order of Family determines the declaration order in the + // memory layout. + enum Family { + REPEATED = 0, + STRING = 1, + MESSAGE = 2, + ZERO_INITIALIZABLE = 3, + OTHER = 4, + kMaxFamily + }; + // First divide fields into those that align to 1 byte, 4 bytes or 8 bytes. - vector<FieldGroup> aligned_to_1, aligned_to_4, aligned_to_8; + vector<FieldGroup> aligned_to_1[kMaxFamily]; + vector<FieldGroup> aligned_to_4[kMaxFamily]; + vector<FieldGroup> aligned_to_8[kMaxFamily]; for (int i = 0; i < fields->size(); ++i) { - switch (EstimateAlignmentSize((*fields)[i])) { - case 1: aligned_to_1.push_back(FieldGroup(i, (*fields)[i])); break; - case 4: aligned_to_4.push_back(FieldGroup(i, (*fields)[i])); break; - case 8: aligned_to_8.push_back(FieldGroup(i, (*fields)[i])); break; - default: - GOOGLE_LOG(FATAL) << "Unknown alignment size."; + const FieldDescriptor* field = (*fields)[i]; + + Family f = OTHER; + if (field->is_repeated()) { + f = REPEATED; + } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { + f = STRING; + } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + f = MESSAGE; + } else if (CanInitializeByZeroing(field)) { + f = ZERO_INITIALIZABLE; } - } - // Now group fields aligned to 1 byte into sets of 4, and treat those like a - // single field aligned to 4 bytes. - for (int i = 0; i < aligned_to_1.size(); i += 4) { - FieldGroup field_group; - for (int j = i; j < aligned_to_1.size() && j < i + 4; ++j) { - field_group.Append(aligned_to_1[j]); + 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; + default: + GOOGLE_LOG(FATAL) << "Unknown alignment size."; } - aligned_to_4.push_back(field_group); } - // Sort by preferred location to keep fields as close to their original - // location as possible. Using stable_sort ensures that the output is - // consistent across runs. - std::stable_sort(aligned_to_4.begin(), aligned_to_4.end()); - // Now group fields aligned to 4 bytes (or the 4-field groups created above) - // into pairs, and treat those like a single field aligned to 8 bytes. - for (int i = 0; i < aligned_to_4.size(); i += 2) { - FieldGroup field_group; - for (int j = i; j < aligned_to_4.size() && j < i + 2; ++j) { - field_group.Append(aligned_to_4[j]); - } - if (i == aligned_to_4.size() - 1) { - // Move incomplete 4-byte block to the end. - field_group.SetPreferredLocation(fields->size() + 1); + // For each family, group fields to optimize padding. + for (int f = 0; f < kMaxFamily; f++) { + // Now group fields aligned to 1 byte into sets of 4, and treat those like a + // single field aligned to 4 bytes. + for (int i = 0; i < aligned_to_1[f].size(); i += 4) { + FieldGroup field_group; + for (int j = i; j < aligned_to_1[f].size() && j < i + 4; ++j) { + field_group.Append(aligned_to_1[f][j]); + } + aligned_to_4[f].push_back(field_group); + } + // Sort by preferred location to keep fields as close to their declaration + // 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()); + + // Now group fields aligned to 4 bytes (or the 4-field groups created above) + // into pairs, and treat those like a single field aligned to 8 bytes. + for (int i = 0; i < aligned_to_4[f].size(); i += 2) { + FieldGroup field_group; + for (int j = i; j < aligned_to_4[f].size() && j < i + 2; ++j) { + field_group.Append(aligned_to_4[f][j]); + } + if (i == aligned_to_4[f].size() - 1) { + if (f == OTHER) { + // Move incomplete 4-byte block to the beginning. This is done to + // pair with the (possible) leftover blocks from the + // ZERO_INITIALIZABLE family. + field_group.SetPreferredLocation(-1); + } else { + // Move incomplete 4-byte block to the end. + field_group.SetPreferredLocation(fields->size() + 1); + } + } + aligned_to_8[f].push_back(field_group); } - aligned_to_8.push_back(field_group); + // Sort by preferred location. + std::stable_sort(aligned_to_8[f].begin(), aligned_to_8[f].end()); } - // Sort by preferred location. - std::stable_sort(aligned_to_8.begin(), aligned_to_8.end()); // Now pull out all the FieldDescriptors in order. fields->clear(); - for (int i = 0; i < aligned_to_8.size(); ++i) { - fields->insert(fields->end(), - aligned_to_8[i].fields().begin(), - aligned_to_8[i].fields().end()); + for (int f = 0; f < kMaxFamily; ++f) { + for (int i = 0; i < aligned_to_8[f].size(); ++i) { + fields->insert(fields->end(), + aligned_to_8[f][i].fields().begin(), + aligned_to_8[f][i].fields().end()); + } } } @@ -337,6 +427,20 @@ bool HasHasMethod(const FieldDescriptor* field) { return field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE; } +size_t HasBitsSize(const Descriptor* descriptor) { + // TODO(jieluo) - Optimize _has_bits_ for repeated and oneof fields. + size_t sizeof_has_bits = (descriptor->field_count() + 31) / 32 * 4; + if (descriptor->field_count() == 0) { + // Zero-size arrays aren't technically allowed, and MSVC in particular + // doesn't like them. We still need to declare these arrays to make + // other code compile. Since this is an uncommon case, we'll just declare + // them with size 1 and waste some space. Oh well. + sizeof_has_bits = 4; + } + + return sizeof_has_bits; +} + // Collects map entry message type information. void CollectMapInfo(const Descriptor* descriptor, map<string, string>* variables) { @@ -389,6 +493,15 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, ExtensionGenerator>[descriptor->extension_count()]), use_dependent_base_(false) { + // 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)); + } + } + OptimizePadding(&optimized_order_); + for (int i = 0; i < descriptor->nested_type_count(); i++) { nested_generators_[i].reset( new MessageGenerator(descriptor->nested_type(i), options)); @@ -644,11 +757,12 @@ GenerateSingularFieldHasBits(const FieldDescriptor* field, " return !$name$_.IsCleared();\n" "}\n"); } else { - printer->Print(vars, - "$inline$" - "bool $classname$::has_$name$() const {\n" - " return !_is_default_instance_ && $name$_ != NULL;\n" - "}\n"); + printer->Print( + vars, + "$inline$" + "bool $classname$::has_$name$() const {\n" + " return this != internal_default_instance() && $name$_ != NULL;\n" + "}\n"); } } } @@ -805,31 +919,6 @@ GenerateFieldAccessorDefinitions(io::Printer* printer, bool is_inline) { } } -// Helper for the code that emits the Clear() method. -static bool CanClearByZeroing(const FieldDescriptor* field) { - if (field->is_repeated() || field->is_extension()) return false; - switch (field->cpp_type()) { - case internal::WireFormatLite::CPPTYPE_ENUM: - return field->default_value_enum()->number() == 0; - case internal::WireFormatLite::CPPTYPE_INT32: - return field->default_value_int32() == 0; - case internal::WireFormatLite::CPPTYPE_INT64: - return field->default_value_int64() == 0; - case internal::WireFormatLite::CPPTYPE_UINT32: - return field->default_value_uint32() == 0; - case internal::WireFormatLite::CPPTYPE_UINT64: - return field->default_value_uint64() == 0; - case internal::WireFormatLite::CPPTYPE_FLOAT: - return field->default_value_float() == 0; - case internal::WireFormatLite::CPPTYPE_DOUBLE: - return field->default_value_double() == 0; - case internal::WireFormatLite::CPPTYPE_BOOL: - return field->default_value_bool() == false; - default: - return false; - } -} - void MessageGenerator:: GenerateDependentBaseClassDefinition(io::Printer* printer) { if (!use_dependent_base_) { @@ -1009,19 +1098,13 @@ GenerateClassDefinition(io::Printer* printer) { "\n"); } - if (!StaticInitializersForced(descriptor_->file(), options_)) { - printer->Print(vars, - "#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER\n" - "// Returns the internal default instance pointer. This function can\n" - "// return NULL thus should not be used by the user. This is intended\n" - "// for Protobuf internal code. Please use default_instance() declared\n" - "// above instead.\n" + // TODO(gerbens) make this private, while still granting other protos access. + printer->Print( + vars, "static inline const $classname$* internal_default_instance() {\n" - " return default_instance_;\n" + " return &default_instance_.get();\n" "}\n" - "#endif\n" "\n"); - } if (SupportsArenas(descriptor_)) { @@ -1068,7 +1151,7 @@ GenerateClassDefinition(io::Printer* printer) { "void Clear();\n" "bool IsInitialized() const;\n" "\n" - "int ByteSize() const;\n" + "size_t ByteSizeLong() const;\n" "bool MergePartialFromCodedStream(\n" " ::google::protobuf::io::CodedInputStream* input);\n" "void SerializeWithCachedSizes(\n" @@ -1131,7 +1214,8 @@ GenerateClassDefinition(io::Printer* printer) { "void SharedCtor();\n" "void SharedDtor();\n" "void SetCachedSize(int size) const;\n" - "void InternalSwap($classname$* other);\n", + "void InternalSwap($classname$* other);\n" + "void UnsafeMergeFrom(const $classname$& from);\n", "classname", classname_); if (SupportsArenas(descriptor_)) { printer->Print( @@ -1260,8 +1344,8 @@ GenerateClassDefinition(io::Printer* printer) { !descriptor_->options().message_set_wire_format() && num_required_fields_ > 1) { printer->Print( - "// helper for ByteSize()\n" - "int RequiredFieldsByteSizeFallback() const;\n\n"); + "// helper for ByteSizeLong()\n" + "size_t RequiredFieldsByteSizeFallback() const;\n\n"); } // Prepare decls for _cached_size_ and _has_bits_. Their position in the @@ -1271,18 +1355,10 @@ GenerateClassDefinition(io::Printer* printer) { // TODO(kenton): Make _cached_size_ an atomic<int> when C++ supports it. const string cached_size_decl = "mutable int _cached_size_;\n"; - // TODO(jieluo) - Optimize _has_bits_ for repeated and oneof fields. - size_t sizeof_has_bits = (descriptor_->field_count() + 31) / 32 * 4; - if (descriptor_->field_count() == 0) { - // Zero-size arrays aren't technically allowed, and MSVC in particular - // doesn't like them. We still need to declare these arrays to make - // other code compile. Since this is an uncommon case, we'll just declare - // them with size 1 and waste some space. Oh well. - sizeof_has_bits = 4; - } + const size_t sizeof_has_bits = HasBitsSize(descriptor_); const string has_bits_decl = sizeof_has_bits == 0 ? "" : - "::google::protobuf::uint32 _has_bits_[" + SimpleItoa(sizeof_has_bits / 4) + "];\n"; - + "::google::protobuf::internal::HasBits<" + SimpleItoa(sizeof_has_bits / 4) + + "> _has_bits_;\n"; // To minimize padding, data members are divided into three sections: // (1) members assumed to align to 8 bytes @@ -1326,45 +1402,27 @@ GenerateClassDefinition(io::Printer* printer) { printer->Print(cached_size_decl.c_str()); need_to_emit_cached_size = false; } - } else { - // Without field presence, we need another way to disambiguate the default - // instance, because the default instance's submessage fields (if any) store - // pointers to the default instances of the submessages even when they - // aren't present. Alternatives to this approach might be to (i) use a - // tagged pointer on all message fields, setting a tag bit for "not really - // present, just default instance"; or (ii) comparing |this| against the - // return value from GeneratedMessageFactory::GetPrototype() in all - // has_$field$() calls. However, both of these options are much more - // expensive (in code size and CPU overhead) than just checking a field in - // the message. Long-term, the best solution would be to rearchitect the - // default instance design not to store pointers to submessage default - // instances, and have reflection get those some other way; but that change - // would have too much impact on proto2. - printer->Print( - "bool _is_default_instance_;\n"); } // Field members: // List fields which doesn't belong to any oneof - vector<const FieldDescriptor*> fields; hash_map<string, int> fieldname_to_chunk; for (int i = 0; i < descriptor_->field_count(); i++) { if (!descriptor_->field(i)->containing_oneof()) { const FieldDescriptor* field = descriptor_->field(i); - fields.push_back(field); fieldname_to_chunk[FieldName(field)] = i / 8; } } - OptimizePadding(&fields); + // Emit some private and static members runs_of_fields_ = vector< vector<string> >(1); - for (int i = 0; i < fields.size(); ++i) { - const FieldDescriptor* field = fields[i]; + for (int i = 0; i < optimized_order_.size(); ++i) { + const FieldDescriptor* field = optimized_order_[i]; const FieldGenerator& generator = field_generators_.get(field); generator.GenerateStaticMembers(printer); generator.GeneratePrivateMembers(printer); - if (CanClearByZeroing(field)) { + if (CanInitializeByZeroing(field)) { const string& fieldname = FieldName(field); if (!runs_of_fields_.back().empty() && (fieldname_to_chunk[runs_of_fields_.back().back()] != @@ -1424,15 +1482,15 @@ GenerateClassDefinition(io::Printer* printer) { // Declare AddDescriptors(), BuildDescriptors(), and ShutdownFile() as // friends so that they can access private static variables like // default_instance_ and reflection_. - PrintHandlingOptionalStaticInitializers( - descriptor_->file(), options_, printer, - // With static initializers. - "friend void $dllexport_decl$ $adddescriptorsname$();\n", - // Without. - "friend void $dllexport_decl$ $adddescriptorsname$_impl();\n", - // Vars. - "dllexport_decl", options_.dllexport_decl, "adddescriptorsname", - GlobalAddDescriptorsName(descriptor_->file()->name())); + printer->Print("friend void $dllexport_decl$ $initdefaultsname$_impl();\n", + // Vars. + "dllexport_decl", options_.dllexport_decl, "initdefaultsname", + GlobalInitDefaultsName(descriptor_->file()->name())); + printer->Print("friend void $dllexport_decl$ $adddescriptorsname$_impl();\n", + // Vars. + "dllexport_decl", options_.dllexport_decl, + "adddescriptorsname", + GlobalAddDescriptorsName(descriptor_->file()->name())); printer->Print( "friend void $assigndescriptorsname$();\n" @@ -1443,12 +1501,12 @@ GenerateClassDefinition(io::Printer* printer) { "shutdownfilename", GlobalShutdownFileName(descriptor_->file()->name())); printer->Print( - "void InitAsDefaultInstance();\n" - "static $classname$* default_instance_;\n", - "classname", classname_); + "void InitAsDefaultInstance();\n" + "static ::google::protobuf::internal::ExplicitlyConstructed<$classname$> default_instance_;\n", + "classname", classname_); printer->Outdent(); - printer->Print(vars, "};"); + printer->Print("};"); GOOGLE_DCHECK(!need_to_emit_cached_size); } @@ -1578,18 +1636,18 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { "::NewGeneratedMessageReflection"; // Construct the reflection object. printer->Print(vars, - "$classname$_reflection_ =\n" - " $fn$(\n" - " $classname$_descriptor_,\n" - " $classname$::default_instance_,\n" - " $classname$_offsets_,\n"); + "$classname$_reflection_ =\n" + " $fn$(\n" + " $classname$_descriptor_,\n" + " $classname$::internal_default_instance(),\n" + " $classname$_offsets_,\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($classname$, _has_bits_[0]),\n"); + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, _has_bits_),\n"); } // Unknown field offset: either points to the unknown field set if embedded @@ -1637,22 +1695,12 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { // not (because e.g. we don't have an unknown field set). if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print(vars, - " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" - "$classname$, _internal_metadata_),\n"); + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" + "$classname$, _internal_metadata_));\n"); } else { printer->Print(vars, - " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" - "$classname$, _arena_),\n"); - } - - // is_default_instance_ offset. - if (HasFieldPresence(descriptor_->file())) { - printer->Print(vars, - " -1);\n"); - } else { - printer->Print(vars, - " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" - "$classname$, _is_default_instance_));\n"); + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" + "$classname$, _arena_));\n"); } // Handle nested types. @@ -1670,11 +1718,11 @@ GenerateTypeRegistrations(io::Printer* printer) { // Register this message type with the message factory. if (!IsMapEntryMessage(descriptor_)) { printer->Print( - "::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(\n" - " $classname$_descriptor_, &$classname$::default_instance());\n", - "classname", classname_); - } - else { + "::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(\n" + " $classname$_descriptor_, " + "$classname$::internal_default_instance());\n", + "classname", classname_); + } else { map<string, string> vars; CollectMapInfo(descriptor_, &vars); vars["classname"] = classname_; @@ -1708,6 +1756,12 @@ GenerateTypeRegistrations(io::Printer* printer) { void MessageGenerator:: GenerateDefaultInstanceAllocator(io::Printer* printer) { + // Force initialization of primitive values we depend on. + printer->Print( + StrCat( + uses_string_ ? "::google::protobuf::internal::GetEmptyString();\n" : "") + .c_str()); + // 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++) { @@ -1720,9 +1774,8 @@ GenerateDefaultInstanceAllocator(io::Printer* printer) { // Construct the default instance. We can't call InitAsDefaultInstance() yet // because we need to make sure all default instances that this one might // depend on are constructed first. - printer->Print( - "$classname$::default_instance_ = new $classname$();\n", - "classname", classname_); + printer->Print("$classname$::default_instance_.DefaultConstruct();\n", + "classname", classname_); if ((descriptor_->oneof_decl_count() > 0) && HasDescriptorMethods(descriptor_->file(), options_)) { @@ -1741,8 +1794,9 @@ GenerateDefaultInstanceAllocator(io::Printer* printer) { void MessageGenerator:: GenerateDefaultInstanceInitializer(io::Printer* printer) { printer->Print( - "$classname$::default_instance_->InitAsDefaultInstance();\n", - "classname", classname_); + "$classname$::default_instance_.get_mutable()->InitAsDefaultInstance();" + "\n", + "classname", classname_); // Register extensions. for (int i = 0; i < descriptor_->extension_count(); i++) { @@ -1761,10 +1815,6 @@ GenerateDefaultInstanceInitializer(io::Printer* printer) { void MessageGenerator:: GenerateShutdownCode(io::Printer* printer) { - printer->Print( - "delete $classname$::default_instance_;\n", - "classname", classname_); - if (HasDescriptorMethods(descriptor_->file(), options_)) { if (descriptor_->oneof_decl_count() > 0) { printer->Print( @@ -1894,6 +1944,12 @@ GenerateClassMethods(io::Printer* printer) { GenerateIsInitialized(printer); printer->Print("\n"); + } else { + printer->Print( + "void $classname$::UnsafeMergeFrom(const $classname$& from) {\n" + " MergeFrom(from);\n" + "}\n", + "classname", classname_); } GenerateSwap(printer); @@ -1966,15 +2022,15 @@ GenerateSharedConstructorCode(io::Printer* printer) { "classname", classname_); printer->Indent(); - if (!HasFieldPresence(descriptor_->file())) { - printer->Print( - " _is_default_instance_ = false;\n"); + bool need_to_clear_cached_size = true; + // We reproduce the logic used for laying out _cached_sized_ in the class + // definition, as to initialize it in-order. + if (HasFieldPresence(descriptor_->file()) && + (HasBitsSize(descriptor_) % 8) != 0) { + printer->Print("_cached_size_ = 0;\n"); + need_to_clear_cached_size = false; } - printer->Print(StrCat( - uses_string_ ? "::google::protobuf::internal::GetEmptyString();\n" : "", - "_cached_size_ = 0;\n").c_str()); - if (PreserveUnknownFields(descriptor_) && !UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( @@ -1982,16 +2038,52 @@ GenerateSharedConstructorCode(io::Printer* printer) { " &::google::protobuf::internal::GetEmptyStringAlreadyInited());\n"); } - for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->containing_oneof()) { - field_generators_.get(descriptor_->field(i)) - .GenerateConstructorCode(printer); + const FieldDescriptor* last_start = NULL; + // RunMap maps from fields that start each run to the number of fields in that + // run. This is optimized for the common case that there are very few runs in + // a message and that most of the eligible fields appear together. + typedef hash_map<const FieldDescriptor*, size_t> RunMap; + RunMap runs; + + for (int i = 0; i < optimized_order_.size(); ++i) { + const FieldDescriptor* field = optimized_order_[i]; + // TODO(ckennelly): Scalar messages (raw pointers) can be initialized by + // zero as well. + if (CanInitializeByZeroing(field)) { + if (last_start == NULL) { + last_start = field; + } + + runs[last_start]++; + } else { + last_start = NULL; } } - if (HasFieldPresence(descriptor_->file())) { - printer->Print( - "::memset(_has_bits_, 0, sizeof(_has_bits_));\n"); + for (int i = 0; i < optimized_order_.size(); ++i) { + const FieldDescriptor* field = optimized_order_[i]; + RunMap::const_iterator it = runs.find(field); + + // We only apply the memset technique to runs of more than one field, as + // assignment is better than memset for generated code clarity. + if (it != runs.end() && it->second > 1) { + // Use a memset, then skip run_length fields. + const size_t run_length = it->second; + const string first_field_name = FieldName(field); + const string last_field_name = + FieldName(optimized_order_[i + run_length - 1]); + + printer->Print( + "::memset(&$first$_, 0, reinterpret_cast<char*>(&$last$_) -\n" + " reinterpret_cast<char*>(&$first$_) + sizeof($last$_));\n", + "first", first_field_name, + "last", last_field_name); + + i += run_length - 1; + // ++i at the top of the loop. + } else { + field_generators_.get(field).GenerateConstructorCode(printer); + } } for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { @@ -2000,6 +2092,10 @@ GenerateSharedConstructorCode(io::Printer* printer) { "oneof_name", descriptor_->oneof_decl(i)->name()); } + if (need_to_clear_cached_size) { + printer->Print("_cached_size_ = 0;\n"); + } + printer->Outdent(); printer->Print("}\n\n"); } @@ -2013,7 +2109,8 @@ GenerateSharedDestructorCode(io::Printer* printer) { if (SupportsArenas(descriptor_)) { // Do nothing when the message is allocated in an arena. printer->Print( - "if (GetArenaNoVirtual() != NULL) {\n" + "::google::protobuf::Arena* arena = GetArenaNoVirtual();\n" + "if (arena != NULL) {\n" " return;\n" "}\n" "\n"); @@ -2026,7 +2123,7 @@ GenerateSharedDestructorCode(io::Printer* printer) { printer->Print( "_unknown_fields_.Destroy(\n" " &::google::protobuf::internal::GetEmptyStringAlreadyInited(),\n" - " GetArenaNoVirtual());\n"); + " arena);\n"); } else { printer->Print( "_unknown_fields_.DestroyNoArena(\n" @@ -2035,11 +2132,10 @@ GenerateSharedDestructorCode(io::Printer* printer) { } // Write the destructors for each field except oneof members. - for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->containing_oneof()) { - field_generators_.get(descriptor_->field(i)) - .GenerateDestructorCode(printer); - } + // optimized_order_ does not contain oneof fields. + for (int i = 0; i < optimized_order_.size(); i++) { + const FieldDescriptor* field = optimized_order_[i]; + field_generators_.get(field).GenerateDestructorCode(printer); } // Generate code to destruct oneofs. Clearing should do the work. @@ -2051,13 +2147,6 @@ GenerateSharedDestructorCode(io::Printer* printer) { "oneof_name", descriptor_->oneof_decl(i)->name()); } - PrintHandlingOptionalStaticInitializers( - descriptor_->file(), options_, printer, - // With static initializers. - "if (this != default_instance_) {\n", - // Without. - "if (this != &default_instance()) {\n"); - // We need to delete all embedded messages. // TODO(kenton): If we make unset messages point at default instances // instead of NULL, then it would make sense to move this code into @@ -2069,16 +2158,13 @@ GenerateSharedDestructorCode(io::Printer* printer) { field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { // Skip oneof members if (!field->containing_oneof()) { - printer->Print( - " delete $name$_;\n", - "name", FieldName(field)); + printer->Print("delete $name$_;\n", "name", FieldName(field)); } } } printer->Outdent(); printer->Print( - " }\n" "}\n" "\n"); } @@ -2151,16 +2237,18 @@ GenerateStructors(io::Printer* printer) { } // Initialize member variables with arena constructor. - for (int i = 0; i < descriptor_->field_count(); i++) { - bool has_arena_constructor = descriptor_->field(i)->is_repeated(); + for (int i = 0; i < optimized_order_.size(); i++) { + const FieldDescriptor* field = optimized_order_[i]; + + bool has_arena_constructor = field->is_repeated(); if (has_arena_constructor) { initializer_with_arena += string(",\n ") + - FieldName(descriptor_->field(i)) + string("_(arena)"); + FieldName(field) + string("_(arena)"); } } if (IsAnyMessage(descriptor_)) { - initializer_with_arena += ",\n _any_metadata_(&type_url, &value_)"; + initializer_with_arena += ",\n _any_metadata_(&type_url_, &value_)"; } string initializer_null; @@ -2173,27 +2261,31 @@ GenerateStructors(io::Printer* printer) { printer->Print( "$classname$::$classname$()\n" " : $superclass$()$initializer$ {\n" + " if (this != internal_default_instance()) $initdefaultsname$();\n" " SharedCtor();\n" " // @@protoc_insertion_point(constructor:$full_name$)\n" "}\n", - "classname", classname_, - "superclass", superclass, - "full_name", descriptor_->full_name(), - "initializer", initializer_null); + "classname", classname_, "superclass", superclass, "full_name", + descriptor_->full_name(), "initializer", initializer_null, + "initdefaultsname", GlobalInitDefaultsName(descriptor_->file()->name())); if (SupportsArenas(descriptor_)) { printer->Print( - "\n" "$classname$::$classname$(::google::protobuf::Arena* arena)\n" " : $initializer$ {\n" + // When arenas are used it's safe to assume we have finished + // static init time (protos with arenas are unsafe during static init) + "#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER\n" + " $initdefaultsname$();\n" + "#endif // GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER\n" " SharedCtor();\n" " RegisterArenaDtor(arena);\n" " // @@protoc_insertion_point(arena_constructor:$full_name$)\n" "}\n", - "initializer", initializer_with_arena, - "classname", classname_, - "superclass", superclass, - "full_name", descriptor_->full_name()); + "initializer", initializer_with_arena, "classname", classname_, + "superclass", superclass, "full_name", descriptor_->full_name(), + "initdefaultsname", + GlobalInitDefaultsName(descriptor_->file()->name())); } printer->Print( @@ -2201,11 +2293,6 @@ GenerateStructors(io::Printer* printer) { "void $classname$::InitAsDefaultInstance() {\n", "classname", classname_); - if (!HasFieldPresence(descriptor_->file())) { - printer->Print( - " _is_default_instance_ = true;\n"); - } - // The default instance needs all of its embedded message pointers // cross-linked to other default instances. We can't do this initialization // in the constructor because some other default instances may not have been @@ -2224,11 +2311,7 @@ GenerateStructors(io::Printer* printer) { name = classname_ + "_default_oneof_instance_->"; } name += FieldName(field); - PrintHandlingOptionalStaticInitializers( - descriptor_->file(), options_, printer, - // With static initializers. - " $name$_ = const_cast< $type$*>(&$type$::default_instance());\n", - // Without. + printer->Print( " $name$_ = const_cast< $type$*>(\n" " $type$::internal_default_instance());\n", // Vars. @@ -2262,7 +2345,7 @@ GenerateStructors(io::Printer* printer) { printer->Print(" {\n"); printer->Print( " SharedCtor();\n" - " MergeFrom(from);\n" + " UnsafeMergeFrom(from);\n" " // @@protoc_insertion_point(copy_constructor:$full_name$)\n" "}\n" "\n", @@ -2304,37 +2387,29 @@ GenerateStructors(io::Printer* printer) { if (HasDescriptorMethods(descriptor_->file(), options_) && !descriptor_->options().no_standard_descriptor_accessor()) { printer->Print( - "const ::google::protobuf::Descriptor* $classname$::descriptor() {\n" - " protobuf_AssignDescriptorsOnce();\n" - " return $classname$_descriptor_;\n" - "}\n" - "\n", - "classname", classname_, - "adddescriptorsname", - GlobalAddDescriptorsName(descriptor_->file()->name())); + "const ::google::protobuf::Descriptor* $classname$::descriptor() {\n" + " protobuf_AssignDescriptorsOnce();\n" + " return $classname$_descriptor_;\n" + "}\n" + "\n", + "classname", classname_, "initdefaultsname", + GlobalInitDefaultsName(descriptor_->file()->name())); } printer->Print( - "const $classname$& $classname$::default_instance() {\n", - "classname", classname_); - - PrintHandlingOptionalStaticInitializers( - descriptor_->file(), options_, printer, - // With static initializers. - " if (default_instance_ == NULL) $adddescriptorsname$();\n", - // Without. - " $adddescriptorsname$();\n", - // Vars. - "adddescriptorsname", - GlobalAddDescriptorsName(descriptor_->file()->name())); + "const $classname$& $classname$::default_instance() {\n" + " $initdefaultsname$();\n" + " return *internal_default_instance();\n" + "}\n", + "classname", classname_, "initdefaultsname", + GlobalInitDefaultsName(descriptor_->file()->name())); printer->Print( - " return *default_instance_;\n" - "}\n" - "\n" - "$classname$* $classname$::default_instance_ = NULL;\n" - "\n", - "classname", classname_); + "\n" + "::google::protobuf::internal::ExplicitlyConstructed<$classname$> " + "$classname$::default_instance_;\n" + "\n", + "classname", classname_); if (SupportsArenas(descriptor_)) { printer->Print( @@ -2463,7 +2538,7 @@ GenerateClear(io::Printer* printer) { int count = popcnt(mask); GOOGLE_DCHECK_GE(count, 1); if (count == 1 || - (count <= 4 && count == memset_field_count_for_chunk[i / 8])) { + count == memset_field_count_for_chunk[i / 8]) { // No "if" here because the chunk is trivial. } else { if (HasFieldPresence(descriptor_->file())) { @@ -2540,8 +2615,7 @@ GenerateClear(io::Printer* printer) { if (HasFieldPresence(descriptor_->file())) { // Step 5: Everything else. - printer->Print( - "::memset(_has_bits_, 0, sizeof(_has_bits_));\n"); + printer->Print("_has_bits_.Clear();\n"); } if (PreserveUnknownFields(descriptor_)) { @@ -2584,7 +2658,7 @@ GenerateOneofClear(io::Printer* printer) { "$full_name$)\n"); printer->Indent(); printer->Print(oneof_vars, - "switch($oneofname$_case()) {\n"); + "switch ($oneofname$_case()) {\n"); printer->Indent(); for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); @@ -2639,7 +2713,7 @@ GenerateSwap(io::Printer* printer) { " InternalSwap(other);\n" " } else {\n" " $classname$ temp;\n" - " temp.MergeFrom(*this);\n" + " temp.UnsafeMergeFrom(*this);\n" " CopyFrom(*other);\n" " other->CopyFrom(temp);\n" " }\n" @@ -2715,9 +2789,7 @@ GenerateMergeFrom(io::Printer* printer) { "void $classname$::MergeFrom(const ::google::protobuf::Message& from) {\n" "// @@protoc_insertion_point(generalized_merge_from_start:" "$full_name$)\n" - " if (GOOGLE_PREDICT_FALSE(&from == this)) {\n" - " ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__);\n" - " }\n", + " if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);\n", "classname", classname_, "full_name", descriptor_->full_name()); printer->Indent(); @@ -2726,7 +2798,7 @@ GenerateMergeFrom(io::Printer* printer) { // system, as the GOOGLE_CHECK above ensured that we have the same descriptor // for each message. printer->Print( - "const $classname$* source = \n" + "const $classname$* source =\n" " ::google::protobuf::internal::DynamicCastToGenerated<const $classname$>(\n" " &from);\n" "if (source == NULL) {\n" @@ -2736,7 +2808,7 @@ GenerateMergeFrom(io::Printer* printer) { "} else {\n" "// @@protoc_insertion_point(generalized_merge_from_cast_success:" "$full_name$)\n" - " MergeFrom(*source);\n" + " UnsafeMergeFrom(*source);\n" "}\n", "classname", classname_, "full_name", descriptor_->full_name()); @@ -2758,9 +2830,15 @@ GenerateMergeFrom(io::Printer* printer) { "void $classname$::MergeFrom(const $classname$& from) {\n" "// @@protoc_insertion_point(class_specific_merge_from_start:" "$full_name$)\n" - " if (GOOGLE_PREDICT_FALSE(&from == this)) {\n" - " ::google::protobuf::internal::MergeFromFail(__FILE__, __LINE__);\n" - " }\n", + " if (GOOGLE_PREDICT_TRUE(&from != this)) {\n" + " UnsafeMergeFrom(from);\n" + " } else {\n" + " MergeFromFail(__LINE__);\n" + " }\n" + "}\n" + "\n" + "void $classname$::UnsafeMergeFrom(const $classname$& from) {\n" + " GOOGLE_DCHECK(&from != this);\n", "classname", classname_, "full_name", descriptor_->full_name()); printer->Indent(); @@ -2770,7 +2848,7 @@ GenerateMergeFrom(io::Printer* printer) { const FieldDescriptor* field = descriptor_->field(i); if (field->is_repeated()) { - field_generators_.get(field).GenerateMergingCode(printer); + field_generators_.get(field).GenerateUnsafeMergingCode(printer); } } @@ -2865,7 +2943,8 @@ GenerateMergeFrom(io::Printer* printer) { if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print( "if (from._internal_metadata_.have_unknown_fields()) {\n" - " mutable_unknown_fields()->MergeFrom(from.unknown_fields());\n" + " ::google::protobuf::UnknownFieldSet::MergeToInternalMetdata(\n" + " from.unknown_fields(), &_internal_metadata_);\n" "}\n"); } else { printer->Print( @@ -2911,7 +2990,7 @@ GenerateCopyFrom(io::Printer* printer) { printer->Print( "if (&from == this) return;\n" "Clear();\n" - "MergeFrom(from);\n"); + "UnsafeMergeFrom(from);\n"); printer->Outdent(); printer->Print("}\n"); @@ -2926,13 +3005,9 @@ GenerateMergeFromCodedStream(io::Printer* printer) { " ::google::protobuf::io::CodedInputStream* input) {\n", "classname", classname_); - PrintHandlingOptionalStaticInitializers( - descriptor_->file(), options_, printer, - // With static initializers. - " return _extensions_.ParseMessageSet(input, default_instance_,\n" - " mutable_unknown_fields());\n", - // Without. - " return _extensions_.ParseMessageSet(input, &default_instance(),\n" + printer->Print( + " return _extensions_.ParseMessageSet(input, " + "internal_default_instance(),\n" " mutable_unknown_fields());\n", // Vars. "classname", classname_); @@ -2971,8 +3046,8 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Print("for (;;) {\n"); printer->Indent(); - google::protobuf::scoped_array<const FieldDescriptor * > ordered_fields( - SortFieldsByNumber(descriptor_)); + 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 @@ -3000,7 +3075,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) { // Find repeated messages and groups now, to simplify what follows. hash_set<int> fields_with_parse_loop; - for (int i = 0; i < descriptor_->field_count(); i++) { + for (int i = 0; i < ordered_fields.size(); i++) { const FieldDescriptor* field = ordered_fields[i]; if (field->is_repeated() && (field->type() == FieldDescriptor::TYPE_MESSAGE || @@ -3012,7 +3087,13 @@ GenerateMergeFromCodedStream(io::Printer* printer) { // need_label is true if we generated "goto parse_$name$" while handling the // previous field. bool need_label = false; - for (int i = 0; i < descriptor_->field_count(); i++) { + // Pay attention to whether we are in a run of fields from the same oneof. + // Motivation: it would be unusual to parse multiple values for a single + // oneof, since only the last would be used. + const FieldDescriptor* last_of_current_oneof = NULL; + // The following is valid iff last_of_current_oneof is non-NULL. + int index_of_last_of_current_oneof = -1; + 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; const bool next_field_loops = fields_with_parse_loop.count(i + 1) > 0; @@ -3111,21 +3192,53 @@ GenerateMergeFromCodedStream(io::Printer* printer) { "input->UnsafeDecrementRecursionDepth();\n"); } - // If there are more fields, expect the next one. + // If there are more fields, expect the next one, unless we just parsed + // a oneof and the next field would be from the same oneof. (There's no + // reason to expect something that makes what we just read irrelevant, + // so guess something after the current string of fields from this oneof.) need_label = false; if (!emitted_goto_next_tag) { - if (i + 1 == descriptor_->field_count()) { + // delta is the distance in ordered_fields[] from the current field to + // the field we'll guess is next. + int delta = last_of_current_oneof == NULL + ? 1 + : std::max(index_of_last_of_current_oneof - i, 1); + if (i == index_of_last_of_current_oneof) { + printer->Outdent(); + printer->Print( + " after_$last$:\n", + "last", FieldName(last_of_current_oneof)); + printer->Indent(); + last_of_current_oneof = NULL; + } else if (last_of_current_oneof == NULL) { + delta = 1; + // Check for the unlikely case that delta > 1 is better. + if (field->containing_oneof() != NULL) { + while (i + delta < ordered_fields.size() && + ordered_fields[i + delta]->containing_oneof() == + field->containing_oneof()) { + index_of_last_of_current_oneof = i + delta; + last_of_current_oneof = ordered_fields[i + delta]; + ++delta; + } + } + } + if (delta > 1) { + printer->Print( + "goto after_$last$;\n", + "last", FieldName(last_of_current_oneof)); + } else if (i + delta == descriptor_->field_count()) { // Expect EOF. // TODO(kenton): Expect group end-tag? printer->Print( "if (input->ExpectAtEnd()) goto success;\n"); } else { - const FieldDescriptor* next_field = ordered_fields[i + 1]; + const FieldDescriptor* next_field = ordered_fields[i + delta]; printer->Print( "if (input->ExpectTag($next_tag$)) goto parse_$next_name$;\n", "next_tag", SimpleItoa(WireFormat::MakeTag(next_field)), "next_name", next_field->name()); - need_label = true; + need_label = delta == 1; } } @@ -3179,31 +3292,21 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Print(") {\n"); if (PreserveUnknownFields(descriptor_)) { if (UseUnknownFieldSet(descriptor_->file(), options_)) { - PrintHandlingOptionalStaticInitializers( - descriptor_->file(), options_, printer, - // With static initializers. - " DO_(_extensions_.ParseField(tag, input, default_instance_,\n" - " mutable_unknown_fields()));\n", - // Without. - " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n" + printer->Print( + " DO_(_extensions_.ParseField(tag, input, " + "internal_default_instance(),\n" " mutable_unknown_fields()));\n"); } else { - PrintHandlingOptionalStaticInitializers( - descriptor_->file(), options_, printer, - // With static initializers. - " DO_(_extensions_.ParseField(tag, input, default_instance_,\n" - " &unknown_fields_stream));\n", - // Without. - " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n" + printer->Print( + " DO_(_extensions_.ParseField(tag, input, " + "internal_default_instance(),\n" " &unknown_fields_stream));\n"); } } else { - PrintHandlingOptionalStaticInitializers( - descriptor_->file(), options_, printer, + printer->Print( // With static initializers. - " DO_(_extensions_.ParseField(tag, input, default_instance_);\n", - // Without. - " DO_(_extensions_.ParseField(tag, input, &default_instance());\n"); + " DO_(_extensions_.ParseField(tag, input, " + "internal_default_instance());\n"); } printer->Print( " continue;\n" @@ -3248,6 +3351,43 @@ GenerateMergeFromCodedStream(io::Printer* printer) { "}\n", "full_name", descriptor_->full_name()); } +void MessageGenerator::GenerateSerializeOneofFields( + io::Printer* printer, const vector<const FieldDescriptor*>& fields, + bool to_array) { + GOOGLE_CHECK(!fields.empty()); + if (fields.size() == 1) { + GenerateSerializeOneField(printer, fields[0], to_array); + return; + } + // We have multiple mutually exclusive choices. Emit a switch statement. + const OneofDescriptor* oneof = fields[0]->containing_oneof(); + printer->Print( + "switch ($oneofname$_case()) {\n", + "oneofname", oneof->name()); + printer->Indent(); + for (int i = 0; i < fields.size(); i++) { + const FieldDescriptor* field = fields[i]; + printer->Print( + "case k$field_name$:\n", + "field_name", UnderscoresToCamelCase(field->name(), true)); + printer->Indent(); + if (to_array) { + field_generators_.get(field).GenerateSerializeWithCachedSizesToArray( + printer); + } else { + field_generators_.get(field).GenerateSerializeWithCachedSizes(printer); + } + printer->Print( + "break;\n"); + printer->Outdent(); + } + printer->Outdent(); + // Doing nothing is an option. + printer->Print( + " default: ;\n" + "}\n"); +} + void MessageGenerator::GenerateSerializeOneField( io::Printer* printer, const FieldDescriptor* field, bool to_array) { PrintFieldComment(printer, field); @@ -3382,10 +3522,61 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) { void MessageGenerator:: GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) { - google::protobuf::scoped_array<const FieldDescriptor * > ordered_fields( - SortFieldsByNumber(descriptor_)); + // If there are multiple fields in a row from the same oneof then we + // coalesce them and emit a switch statement. This is more efficient + // because it lets the C++ compiler know this is a "at most one can happen" + // situation. If we emitted "if (has_x()) ...; if (has_y()) ..." the C++ + // compiler's emitted code might check has_y() even when has_x() is true. + class LazySerializerEmitter { + public: + LazySerializerEmitter(MessageGenerator* mg, io::Printer* printer, + bool to_array) + : mg_(mg), + printer_(printer), + to_array_(to_array), + eager_(!HasFieldPresence(mg->descriptor_->file())) {} + + ~LazySerializerEmitter() { Flush(); } + + // If conditions allow, try to accumulate a run of fields from the same + // oneof, and handle them at the next Flush(). + void Emit(const FieldDescriptor* field) { + if (eager_ || MustFlush(field)) { + Flush(); + } + if (field->containing_oneof() == NULL) { + mg_->GenerateSerializeOneField(printer_, field, to_array_); + } else { + v_.push_back(field); + } + } - vector<const Descriptor::ExtensionRange*> sorted_extensions; + void Flush() { + if (!v_.empty()) { + mg_->GenerateSerializeOneofFields(printer_, v_, to_array_); + v_.clear(); + } + } + + private: + // If we have multiple fields in v_ then they all must be from the same + // oneof. Would adding field to v_ break that invariant? + bool MustFlush(const FieldDescriptor* field) { + return !v_.empty() && + v_[0]->containing_oneof() != field->containing_oneof(); + } + + MessageGenerator* mg_; + io::Printer* printer_; + const bool to_array_; + const bool eager_; + vector<const FieldDescriptor*> v_; + }; + + std::vector<const FieldDescriptor*> ordered_fields = + SortFieldsByNumber(descriptor_); + + std::vector<const Descriptor::ExtensionRange*> sorted_extensions; for (int i = 0; i < descriptor_->extension_range_count(); ++i) { sorted_extensions.push_back(descriptor_->extension_range(i)); } @@ -3393,22 +3584,26 @@ GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) { ExtensionRangeSorter()); // Merge the fields and the extension ranges, both sorted by field number. - int i, j; - for (i = 0, j = 0; - i < descriptor_->field_count() || j < sorted_extensions.size(); - ) { - if (i == descriptor_->field_count()) { - GenerateSerializeOneExtensionRange(printer, - sorted_extensions[j++], - to_array); - } else if (j == sorted_extensions.size()) { - GenerateSerializeOneField(printer, ordered_fields[i++], to_array); - } else if (ordered_fields[i]->number() < sorted_extensions[j]->start) { - GenerateSerializeOneField(printer, ordered_fields[i++], to_array); - } else { - GenerateSerializeOneExtensionRange(printer, - sorted_extensions[j++], - to_array); + { + LazySerializerEmitter e(this, printer, to_array); + 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 { + e.Flush(); + GenerateSerializeOneExtensionRange(printer, + sorted_extensions[j++], + to_array); + } } } @@ -3480,9 +3675,9 @@ GenerateByteSize(io::Printer* printer) { if (descriptor_->options().message_set_wire_format()) { // Special-case MessageSet. printer->Print( - "int $classname$::ByteSize() const {\n" + "size_t $classname$::ByteSizeLong() const {\n" "// @@protoc_insertion_point(message_set_byte_size_start:$full_name$)\n" - " int total_size = _extensions_.MessageSetByteSize();\n", + " size_t total_size = _extensions_.MessageSetByteSize();\n", "classname", classname_, "full_name", descriptor_->full_name()); GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file(), options_)); printer->Print( @@ -3491,8 +3686,9 @@ GenerateByteSize(io::Printer* printer) { " ComputeUnknownMessageSetItemsSize(unknown_fields());\n" "}\n"); printer->Print( + " int cached_size = ::google::protobuf::internal::ToCachedSize(total_size);\n" " GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();\n" - " _cached_size_ = total_size;\n" + " _cached_size_ = cached_size;\n" " GOOGLE_SAFE_CONCURRENT_WRITES_END();\n" " return total_size;\n" "}\n"); @@ -3503,12 +3699,12 @@ GenerateByteSize(io::Printer* printer) { // Emit a function (rarely used, we hope) that handles the required fields // by checking for each one individually. printer->Print( - "int $classname$::RequiredFieldsByteSizeFallback() const {\n" + "size_t $classname$::RequiredFieldsByteSizeFallback() const {\n" "// @@protoc_insertion_point(required_fields_byte_size_fallback_start:" "$full_name$)\n", "classname", classname_, "full_name", descriptor_->full_name()); printer->Indent(); - printer->Print("int total_size = 0;\n"); + printer->Print("size_t total_size = 0;\n"); for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); if (field->is_required()) { @@ -3529,12 +3725,12 @@ GenerateByteSize(io::Printer* printer) { } printer->Print( - "int $classname$::ByteSize() const {\n" + "size_t $classname$::ByteSizeLong() const {\n" "// @@protoc_insertion_point(message_byte_size_start:$full_name$)\n", "classname", classname_, "full_name", descriptor_->full_name()); printer->Indent(); printer->Print( - "int total_size = 0;\n" + "size_t total_size = 0;\n" "\n"); // Handle required fields (if any). We expect all of them to be @@ -3727,8 +3923,9 @@ GenerateByteSize(io::Printer* printer) { // exact same value, it works on all common processors. In a future version // of C++, _cached_size_ should be made into an atomic<int>. printer->Print( + "int cached_size = ::google::protobuf::internal::ToCachedSize(total_size);\n" "GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();\n" - "_cached_size_ = total_size;\n" + "_cached_size_ = cached_size;\n" "GOOGLE_SAFE_CONCURRENT_WRITES_END();\n" "return total_size;\n"); @@ -3804,7 +4001,9 @@ GenerateIsInitialized(io::Printer* printer) { if (descriptor_->extension_range_count() > 0) { printer->Print( "\n" - "if (!_extensions_.IsInitialized()) return false;\n"); + "if (!_extensions_.IsInitialized()) {\n" + " return false;\n" + "}\n"); } printer->Outdent(); |