Index: third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_message.cc |
diff --git a/third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_message.cc b/third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_message.cc |
index 3f8e3b7f8dd0fbd48f50483c5eeaace7e236b0fd..5af12575bd48fe5e50f4c88f28d8013aa628e975 100644 |
--- a/third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_message.cc |
+++ b/third_party/protobuf/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_) { |
@@ -838,11 +927,13 @@ GenerateDependentBaseClassDefinition(io::Printer* printer) { |
map<string, string> vars; |
vars["classname"] = DependentBaseClassTemplateName(descriptor_); |
+ vars["full_name"] = descriptor_->full_name(); |
vars["superclass"] = SuperClassName(descriptor_, options_); |
printer->Print(vars, |
"template <class T>\n" |
- "class $classname$ : public $superclass$ {\n" |
+ "class $classname$ : public $superclass$ " |
+ "/* @@protoc_insertion_point(dep_base_class_definition:$full_name$) */ {\n" |
" public:\n"); |
printer->Indent(); |
@@ -878,6 +969,7 @@ GenerateClassDefinition(io::Printer* printer) { |
map<string, string> vars; |
vars["classname"] = classname_; |
+ vars["full_name"] = descriptor_->full_name(); |
vars["field_count"] = SimpleItoa(descriptor_->field_count()); |
vars["oneof_decl_count"] = SimpleItoa(descriptor_->oneof_decl_count()); |
if (options_.dllexport_decl.empty()) { |
@@ -892,7 +984,9 @@ GenerateClassDefinition(io::Printer* printer) { |
vars["superclass"] = SuperClassName(descriptor_, options_); |
} |
printer->Print(vars, |
- "class $dllexport$$classname$ : public $superclass$ {\n"); |
+ "class $dllexport$$classname$ : public $superclass$ " |
+ "/* @@protoc_insertion_point(class_definition:$full_name$) */ " |
+ "{\n"); |
printer->Annotate("classname", descriptor_); |
if (use_dependent_base_) { |
printer->Print(vars, " friend class $superclass$;\n"); |
@@ -1004,19 +1098,11 @@ 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" |
- "static inline const $classname$* internal_default_instance() {\n" |
- " return default_instance_;\n" |
- "}\n" |
- "#endif\n" |
+ // TODO(gerbens) make this private, while still granting other protos access. |
+ printer->Print( |
+ vars, |
+ "static const $classname$* internal_default_instance();\n" |
"\n"); |
- } |
if (SupportsArenas(descriptor_)) { |
@@ -1063,7 +1149,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" |
@@ -1076,7 +1162,11 @@ GenerateClassDefinition(io::Printer* printer) { |
} |
if (HasFastArraySerialization(descriptor_->file(), options_)) { |
printer->Print( |
- "::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;\n"); |
+ "::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(\n" |
+ " bool deterministic, ::google::protobuf::uint8* output) const;\n" |
+ "::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const {\n" |
+ " return InternalSerializeWithCachedSizesToArray(false, output);\n" |
+ "}\n"); |
} |
} |
@@ -1095,6 +1185,13 @@ GenerateClassDefinition(io::Printer* printer) { |
descriptors.push_back(descriptor_->oneof_decl(i)->field(j)); |
} |
} |
+ for (int i = 0; i < descriptor_->nested_type_count(); i++) { |
+ const Descriptor* nested_type = descriptor_->nested_type(i); |
+ if (IsMapEntryMessage(nested_type)) { |
+ descriptors.push_back(nested_type->FindFieldByName("key")); |
+ descriptors.push_back(nested_type->FindFieldByName("value")); |
+ } |
+ } |
uses_string_ = false; |
if (PreserveUnknownFields(descriptor_) && |
!UseUnknownFieldSet(descriptor_->file(), options_)) { |
@@ -1115,7 +1212,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( |
@@ -1244,8 +1342,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 |
@@ -1255,18 +1353,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 |
@@ -1310,45 +1400,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()] != |
@@ -1408,15 +1480,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" |
@@ -1427,12 +1499,22 @@ GenerateClassDefinition(io::Printer* printer) { |
"shutdownfilename", GlobalShutdownFileName(descriptor_->file()->name())); |
printer->Print( |
- "void InitAsDefaultInstance();\n" |
- "static $classname$* default_instance_;\n", |
- "classname", classname_); |
+ "void InitAsDefaultInstance();\n", |
+ "classname", classname_); |
printer->Outdent(); |
- printer->Print(vars, "};"); |
+ printer->Print("};\n"); |
+ |
+ // This should ideally be put into the class scope, but Visual Studio just |
+ // refuses to compile it and complains about "use of undefined XXX": |
+ // https://ci.appveyor.com/project/protobuf/protobuf/build/1.0.2673/job/nrdf4tb9dau0sck5 |
+ // A program as simple as "struct X { enum { value = sizeof(X) }; };" will |
+ // trigger the same error. |
+ printer->Print( |
+ "extern ::google::protobuf::internal::ExplicitlyConstructed<$classname$> " |
+ "$classname$_default_instance_;\n", |
+ "classname", classname_); |
+ |
GOOGLE_DCHECK(!need_to_emit_cached_size); |
} |
@@ -1562,18 +1644,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 |
@@ -1621,22 +1703,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"); |
- } 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"); |
+ " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" |
+ "$classname$, _internal_metadata_));\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. |
@@ -1654,11 +1726,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_; |
@@ -1692,6 +1764,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++) { |
@@ -1704,9 +1782,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_)) { |
@@ -1725,8 +1802,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++) { |
@@ -1746,9 +1824,8 @@ GenerateDefaultInstanceInitializer(io::Printer* printer) { |
void MessageGenerator:: |
GenerateShutdownCode(io::Printer* printer) { |
printer->Print( |
- "delete $classname$::default_instance_;\n", |
- "classname", classname_); |
- |
+ "$classname$_default_instance_.Shutdown();\n", |
+ "classname", classname_); |
if (HasDescriptorMethods(descriptor_->file(), options_)) { |
if (descriptor_->oneof_decl_count() > 0) { |
printer->Print( |
@@ -1878,6 +1955,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); |
@@ -1950,15 +2033,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( |
@@ -1966,16 +2049,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++) { |
@@ -1984,6 +2103,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"); |
} |
@@ -1997,7 +2120,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"); |
@@ -2010,7 +2134,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" |
@@ -2019,11 +2143,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. |
@@ -2035,17 +2158,11 @@ 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 |
// MessageFieldGenerator::GenerateDestructorCode(). |
+ bool need_delete_message_field = false; |
for (int i = 0; i < descriptor_->field_count(); i++) { |
const FieldDescriptor* field = descriptor_->field(i); |
@@ -2053,16 +2170,25 @@ 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)); |
+ if (!need_delete_message_field) { |
+ need_delete_message_field = true; |
+ printer->Print( |
+ "if (this != &$classname$_default_instance_.get()) {\n", |
+ "classname", classname_); |
+ printer->Indent(); |
+ } |
+ printer->Print("delete $name$_;\n", "name", FieldName(field)); |
} |
} |
} |
+ if (need_delete_message_field) { |
+ printer->Outdent(); |
+ printer->Print( |
+ "}\n"); |
+ } |
printer->Outdent(); |
printer->Print( |
- " }\n" |
"}\n" |
"\n"); |
} |
@@ -2135,16 +2261,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; |
@@ -2157,27 +2285,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( |
@@ -2185,11 +2317,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 |
@@ -2208,11 +2335,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. |
@@ -2246,7 +2369,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", |
@@ -2288,37 +2411,35 @@ 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_); |
+ "const $classname$& $classname$::default_instance() {\n" |
+ " $initdefaultsname$();\n" |
+ " return *internal_default_instance();\n" |
+ "}\n", |
+ "classname", classname_, "initdefaultsname", |
+ GlobalInitDefaultsName(descriptor_->file()->name())); |
- PrintHandlingOptionalStaticInitializers( |
- descriptor_->file(), options_, printer, |
- // With static initializers. |
- " if (default_instance_ == NULL) $adddescriptorsname$();\n", |
- // Without. |
- " $adddescriptorsname$();\n", |
- // Vars. |
- "adddescriptorsname", |
- GlobalAddDescriptorsName(descriptor_->file()->name())); |
+ printer->Print( |
+ "\n" |
+ "::google::protobuf::internal::ExplicitlyConstructed<$classname$> " |
+ "$classname$_default_instance_;\n" |
+ "\n", |
+ "classname", classname_); |
printer->Print( |
- " return *default_instance_;\n" |
- "}\n" |
- "\n" |
- "$classname$* $classname$::default_instance_ = NULL;\n" |
- "\n", |
- "classname", classname_); |
+ "const $classname$* $classname$::internal_default_instance() {\n" |
+ " return &$classname$_default_instance_.get();\n" |
+ "}\n", |
+ "classname", classname_); |
if (SupportsArenas(descriptor_)) { |
printer->Print( |
@@ -2403,7 +2524,7 @@ GenerateClear(io::Printer* printer) { |
" &reinterpret_cast<$classname$*>(16)->f)\n" |
"#endif\n\n" |
"#define ZR_(first, last) do {\\\n" |
- " ::memset(&first, 0,\\\n" |
+ " ::memset(&(first), 0,\\\n" |
" ZR_HELPER_(last) - ZR_HELPER_(first) + sizeof(last));\\\n" |
"} while (0)\n\n"; |
for (int i = 0; i < runs_of_fields_.size(); i++) { |
@@ -2447,7 +2568,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())) { |
@@ -2524,8 +2645,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_)) { |
@@ -2568,7 +2688,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); |
@@ -2623,7 +2743,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" |
@@ -2708,7 +2828,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" |
@@ -2718,7 +2838,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()); |
@@ -2740,7 +2860,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)) MergeFromFail(__LINE__);\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(); |
@@ -2750,7 +2878,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); |
} |
} |
@@ -2845,7 +2973,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( |
@@ -2891,7 +3020,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"); |
@@ -2906,13 +3035,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_); |
@@ -2936,7 +3061,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) { |
// on the CodedOutputStream. |
printer->Print( |
" ::google::protobuf::io::LazyStringOutputStream unknown_fields_string(\n" |
- " ::google::protobuf::internal::NewPermanentCallback(\n" |
+ " ::google::protobuf::NewPermanentCallback(\n" |
" &MutableUnknownFieldsFor$classname$, this));\n" |
" ::google::protobuf::io::CodedOutputStream unknown_fields_stream(\n" |
" &unknown_fields_string, false);\n", |
@@ -2951,8 +3076,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 |
@@ -2980,7 +3105,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 || |
@@ -2992,7 +3117,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; |
@@ -3091,21 +3222,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; |
} |
} |
@@ -3159,31 +3322,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" |
@@ -3228,6 +3381,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); |
@@ -3267,8 +3457,8 @@ void MessageGenerator::GenerateSerializeOneExtensionRange( |
"// Extension range [$start$, $end$)\n"); |
if (to_array) { |
printer->Print(vars, |
- "target = _extensions_.SerializeWithCachedSizesToArray(\n" |
- " $start$, $end$, target);\n\n"); |
+ "target = _extensions_.InternalSerializeWithCachedSizesToArray(\n" |
+ " $start$, $end$, false, target);\n\n"); |
} else { |
printer->Print(vars, |
"_extensions_.SerializeWithCachedSizes(\n" |
@@ -3320,10 +3510,11 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) { |
if (descriptor_->options().message_set_wire_format()) { |
// Special-case MessageSet. |
printer->Print( |
- "::google::protobuf::uint8* $classname$::SerializeWithCachedSizesToArray(\n" |
- " ::google::protobuf::uint8* target) const {\n" |
- " target =\n" |
- " _extensions_.SerializeMessageSetWithCachedSizesToArray(target);\n", |
+ "::google::protobuf::uint8* $classname$::InternalSerializeWithCachedSizesToArray(\n" |
+ " bool deterministic, ::google::protobuf::uint8* target) const {\n" |
+ " target = _extensions_." |
+ "InternalSerializeMessageSetWithCachedSizesToArray(\n" |
+ " deterministic, target);\n", |
"classname", classname_); |
GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file(), options_)); |
printer->Print( |
@@ -3337,11 +3528,12 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) { |
} |
printer->Print( |
- "::google::protobuf::uint8* $classname$::SerializeWithCachedSizesToArray(\n" |
- " ::google::protobuf::uint8* target) const {\n", |
+ "::google::protobuf::uint8* $classname$::InternalSerializeWithCachedSizesToArray(\n" |
+ " bool deterministic, ::google::protobuf::uint8* target) const {\n", |
"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()); |
@@ -3360,10 +3552,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); |
+ } |
+ } |
+ |
+ void Flush() { |
+ if (!v_.empty()) { |
+ mg_->GenerateSerializeOneofFields(printer_, v_, to_array_); |
+ v_.clear(); |
+ } |
+ } |
- vector<const Descriptor::ExtensionRange*> sorted_extensions; |
+ 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)); |
} |
@@ -3371,22 +3614,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); |
+ } |
} |
} |
@@ -3458,9 +3705,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( |
@@ -3469,8 +3716,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"); |
@@ -3481,12 +3729,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()) { |
@@ -3507,12 +3755,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 |
@@ -3705,8 +3953,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"); |
@@ -3782,7 +4031,9 @@ GenerateIsInitialized(io::Printer* printer) { |
if (descriptor_->extension_range_count() > 0) { |
printer->Print( |
"\n" |
- "if (!_extensions_.IsInitialized()) return false;"); |
+ "if (!_extensions_.IsInitialized()) {\n" |
+ " return false;\n" |
+ "}\n"); |
} |
printer->Outdent(); |