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