aboutsummaryrefslogtreecommitdiff
path: root/src/google/protobuf/compiler/cpp/cpp_message.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/google/protobuf/compiler/cpp/cpp_message.cc')
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_message.cc855
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();