diff options
Diffstat (limited to 'src/google/protobuf/compiler/cpp/cpp_message.cc')
-rw-r--r-- | src/google/protobuf/compiler/cpp/cpp_message.cc | 1014 |
1 files changed, 797 insertions, 217 deletions
diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index 3a9d2639..54a92ae4 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -36,6 +36,9 @@ #include <google/protobuf/stubs/hash.h> #include <map> #include <memory> +#ifndef _SHARED_PTR_H +#include <google/protobuf/stubs/shared_ptr.h> +#endif #include <set> #include <utility> #include <vector> @@ -281,8 +284,67 @@ 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 +// without true field presence. Should only be called if +// !HasFieldPresence(message_descriptor). +bool EmitFieldNonDefaultCondition(io::Printer* printer, + const string& prefix, + const FieldDescriptor* field) { + // Merge and serialize semantics: primitive fields are merged/serialized only + // if non-zero (numeric) or non-empty (string). + if (!field->is_repeated() && !field->containing_oneof()) { + if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { + printer->Print( + "if ($prefix$$name$().size() > 0) {\n", + "prefix", prefix, + "name", FieldName(field)); + } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + // Message fields still have has_$name$() methods. + printer->Print( + "if ($prefix$has_$name$()) {\n", + "prefix", prefix, + "name", FieldName(field)); + } else { + printer->Print( + "if ($prefix$$name$() != 0) {\n", + "prefix", prefix, + "name", FieldName(field)); + } + printer->Indent(); + return true; + } else if (field->containing_oneof()) { + printer->Print( + "if (has_$name$()) {\n", + "name", FieldName(field)); + printer->Indent(); + return true; + } + return false; +} + +// Does the given field have a has_$name$() method? +bool HasHasMethod(const FieldDescriptor* field) { + if (HasFieldPresence(field->file())) { + // In proto1/proto2, every field has a has_$name$() method. + return true; + } + // For message types without true field presence, only fields with a message + // type have a has_$name$() method. + return field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE; } +// Does the given field have a private (internal helper only) has_$name$() +// method? +bool HasPrivateHasMethod(const FieldDescriptor* field) { + // Only for oneofs in message types with no field presence. has_$name$(), + // based on the oneof case, is still useful internally for generated code. + return (!HasFieldPresence(field->file()) && + field->containing_oneof() != NULL); +} + +} // anonymous namespace + // =================================================================== MessageGenerator::MessageGenerator(const Descriptor* descriptor, @@ -312,6 +374,13 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, extension_generators_[i].reset( new ExtensionGenerator(descriptor->extension(i), options)); } + + num_required_fields_ = 0; + for (int i = 0; i < descriptor->field_count(); i++) { + if (descriptor->field(i)->is_required()) { + ++num_required_fields_; + } + } } MessageGenerator::~MessageGenerator() {} @@ -360,8 +429,13 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) { if (field->is_repeated()) { printer->Print(vars, "inline int $name$_size() const$deprecation$;\n"); - } else { + } else if (HasHasMethod(field)) { printer->Print(vars, "inline bool has_$name$() const$deprecation$;\n"); + } else if (HasPrivateHasMethod(field)) { + printer->Print(vars, + "private:\n" + "inline bool has_$name$() const$deprecation$;\n" + "public:\n"); } printer->Print(vars, "inline void clear_$name$()$deprecation$;\n"); @@ -410,32 +484,59 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) { "}\n"); } else if (field->containing_oneof()) { // Singular field in a oneof + // N.B.: Without field presence, we do not use has-bits or generate + // has_$name$() methods, but oneofs still have set_has_$name$(). + // Oneofs also have has_$name$() but only as a private helper + // method, so that generated code is slightly cleaner (vs. comparing + // _oneof_case_[index] against a constant everywhere). vars["field_name"] = UnderscoresToCamelCase(field->name(), true); vars["oneof_name"] = field->containing_oneof()->name(); vars["oneof_index"] = SimpleItoa(field->containing_oneof()->index()); printer->Print(vars, "inline bool $classname$::has_$name$() const {\n" " return $oneof_name$_case() == k$field_name$;\n" - "}\n" + "}\n"); + printer->Print(vars, "inline void $classname$::set_has_$name$() {\n" " _oneof_case_[$oneof_index$] = k$field_name$;\n" "}\n"); } else { // Singular field. - char buffer[kFastToBufferSize]; - vars["has_array_index"] = SimpleItoa(field->index() / 32); - vars["has_mask"] = FastHex32ToBuffer(1u << (field->index() % 32), buffer); - printer->Print(vars, - "inline bool $classname$::has_$name$() const {\n" - " return (_has_bits_[$has_array_index$] & 0x$has_mask$u) != 0;\n" - "}\n" - "inline void $classname$::set_has_$name$() {\n" - " _has_bits_[$has_array_index$] |= 0x$has_mask$u;\n" - "}\n" - "inline void $classname$::clear_has_$name$() {\n" - " _has_bits_[$has_array_index$] &= ~0x$has_mask$u;\n" - "}\n" - ); + if (HasFieldPresence(descriptor_->file())) { + // N.B.: without field presence, we do not use has-bits or generate + // has_$name$() methods. + char buffer[kFastToBufferSize]; + vars["has_array_index"] = SimpleItoa(field->index() / 32); + vars["has_mask"] = FastHex32ToBuffer(1u << (field->index() % 32), + buffer); + printer->Print(vars, + "inline bool $classname$::has_$name$() const {\n" + " return (_has_bits_[$has_array_index$] & 0x$has_mask$u) != 0;\n" + "}\n" + "inline void $classname$::set_has_$name$() {\n" + " _has_bits_[$has_array_index$] |= 0x$has_mask$u;\n" + "}\n" + "inline void $classname$::clear_has_$name$() {\n" + " _has_bits_[$has_array_index$] &= ~0x$has_mask$u;\n" + "}\n" + ); + } else { + // Message fields have a has_$name$() method. + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + bool is_lazy = false; + if (is_lazy) { + printer->Print(vars, + "inline bool $classname$::has_$name$() const {\n" + " return !$name$_.IsCleared();\n" + "}\n"); + } else { + printer->Print(vars, + "inline bool $classname$::has_$name$() const {\n" + " return $name$_ != NULL;\n" + "}\n"); + } + } + } } // Generate clear_$name$() @@ -457,9 +558,11 @@ GenerateFieldAccessorDefinitions(io::Printer* printer) { printer->Print("}\n"); } else { field_generators_.get(field).GenerateClearingCode(printer); - if (!field->is_repeated()) { - printer->Print(vars, - "clear_has_$name$();\n"); + if (HasFieldPresence(descriptor_->file())) { + if (!field->is_repeated()) { + printer->Print(vars, + "clear_has_$name$();\n"); + } } } @@ -553,26 +656,41 @@ GenerateClassDefinition(io::Printer* printer) { "}\n" "\n"); - if (UseUnknownFieldSet(descriptor_->file())) { - printer->Print( - "inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {\n" - " return _unknown_fields_;\n" - "}\n" - "\n" - "inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {\n" - " return &_unknown_fields_;\n" - "}\n" - "\n"); - } else { + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {\n" + " return _internal_metadata_.unknown_fields();\n" + "}\n" + "\n" + "inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {\n" + " return _internal_metadata_.mutable_unknown_fields();\n" + "}\n" + "\n"); + } else { + printer->Print( + "inline const ::std::string& unknown_fields() const {\n" + " return _unknown_fields_;\n" + "}\n" + "\n" + "inline ::std::string* mutable_unknown_fields() {\n" + " return &_unknown_fields_;\n" + "}\n" + "\n"); + } + } + + // N.B.: We exclude GetArena() when arena support is disabled, falling back on + // MessageLite's implementation which returns NULL rather than generating our + // own method which returns NULL, in order to reduce code size. + if (SupportsArenas(descriptor_)) { + // virtual method version of GetArenaNoVirtual(), required for generic dispatch given a + // MessageLite* (e.g., in RepeatedField::AddAllocated()). printer->Print( - "inline const ::std::string& unknown_fields() const {\n" - " return _unknown_fields_;\n" - "}\n" - "\n" - "inline ::std::string* mutable_unknown_fields() {\n" - " return &_unknown_fields_;\n" - "}\n" - "\n"); + "inline ::google::protobuf::Arena* GetArena() const { return GetArenaNoVirtual(); }\n" + "inline void* GetMaybeArenaPointer() const {\n" + " return MaybeArenaPtr();\n" + "}\n"); } // Only generate this member if it's not disabled. @@ -628,12 +746,18 @@ GenerateClassDefinition(io::Printer* printer) { } + if (SupportsArenas(descriptor_)) { + printer->Print(vars, + "void UnsafeArenaSwap($classname$* other);\n"); + } printer->Print(vars, "void Swap($classname$* other);\n" "\n" "// implements Message ----------------------------------------------\n" "\n" - "$classname$* New() const;\n"); + "inline $classname$* New() const { return New(NULL); }\n" + "\n" + "$classname$* New(::google::protobuf::Arena* arena) const;\n"); if (HasGeneratedMethods(descriptor_->file())) { if (HasDescriptorMethods(descriptor_->file())) { @@ -699,7 +823,41 @@ GenerateClassDefinition(io::Printer* printer) { "void SharedCtor();\n" "void SharedDtor();\n" "void SetCachedSize(int size) const;\n" - "public:\n"); + "void InternalSwap($classname$* other);\n", + "classname", classname_); + if (SupportsArenas(descriptor_)) { + printer->Print( + "protected:\n" + "explicit $classname$(::google::protobuf::Arena* arena);\n" + "private:\n" + "static void ArenaDtor(void* object);\n" + "inline void RegisterArenaDtor(::google::protobuf::Arena* arena);\n", + "classname", classname_); + } + + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "private:\n" + "inline ::google::protobuf::Arena* GetArenaNoVirtual() const {\n" + " return _internal_metadata_.arena();\n" + "}\n" + "inline void* MaybeArenaPtr() const {\n" + " return _internal_metadata_.raw_arena_ptr();\n" + "}\n" + "public:\n" + "\n"); + } else { + printer->Print( + "private:\n" + "inline ::google::protobuf::Arena* GetArenaNoVirtual() const {\n" + " return _arena_ptr_;\n" + "}\n" + "inline ::google::protobuf::Arena* MaybeArenaPtr() const {\n" + " return _arena_ptr_;\n" + "}\n" + "public:\n" + "\n"); + } if (HasDescriptorMethods(descriptor_->file())) { printer->Print( @@ -759,10 +917,18 @@ GenerateClassDefinition(io::Printer* printer) { for (int i = 0; i < descriptor_->field_count(); i++) { if (!descriptor_->field(i)->is_repeated()) { - printer->Print( - "inline void set_has_$name$();\n", - "name", FieldName(descriptor_->field(i))); - if (!descriptor_->field(i)->containing_oneof()) { + // set_has_***() generated in all proto1/2 code and in oneofs (only) for + // messages without true field presence. + if (HasFieldPresence(descriptor_->file()) || + descriptor_->field(i)->containing_oneof()) { + printer->Print( + "inline void set_has_$name$();\n", + "name", FieldName(descriptor_->field(i))); + } + // clear_has_***() generated only for non-oneof fields + // in proto1/2. + if (!descriptor_->field(i)->containing_oneof() && + HasFieldPresence(descriptor_->file())) { printer->Print( "inline void clear_has_$name$();\n", "name", FieldName(descriptor_->field(i))); @@ -780,6 +946,14 @@ GenerateClassDefinition(io::Printer* printer) { "oneof_name", descriptor_->oneof_decl(i)->name()); } + if (HasGeneratedMethods(descriptor_->file()) && + !descriptor_->options().message_set_wire_format() && + num_required_fields_ > 1) { + printer->Print( + "// helper for ByteSize()\n" + "int RequiredFieldsByteSizeFallback() const;\n\n"); + } + // Prepare decls for _cached_size_ and _has_bits_. Their position in the // output will be determined later. @@ -816,23 +990,32 @@ GenerateClassDefinition(io::Printer* printer) { if (UseUnknownFieldSet(descriptor_->file())) { printer->Print( - "::google::protobuf::UnknownFieldSet _unknown_fields_;\n" - "\n"); + "::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;\n"); } else { printer->Print( "::std::string _unknown_fields_;\n" + "::google::protobuf::Arena* _arena_ptr_;\n" "\n"); } - // _has_bits_ is frequently accessed, so to reduce code size and improve - // speed, it should be close to the start of the object. But, try not to - // waste space:_has_bits_ by itself always makes sense if its size is a - // multiple of 8, but, otherwise, maybe _has_bits_ and cached_size_ together - // will work well. - printer->Print(has_bits_decl.c_str()); - if ((sizeof_has_bits % 8) != 0) { - printer->Print(cached_size_decl.c_str()); - need_to_emit_cached_size = false; + if (SupportsArenas(descriptor_)) { + printer->Print( + "friend class ::google::protobuf::Arena;\n" + "typedef void InternalArenaConstructable_;\n" + "typedef void DestructorSkippable_;\n"); + } + + if (HasFieldPresence(descriptor_->file())) { + // _has_bits_ is frequently accessed, so to reduce code size and improve + // speed, it should be close to the start of the object. But, try not to + // waste space:_has_bits_ by itself always makes sense if its size is a + // multiple of 8, but, otherwise, maybe _has_bits_ and cached_size_ together + // will work well. + printer->Print(has_bits_decl.c_str()); + if ((sizeof_has_bits % 8) != 0) { + printer->Print(cached_size_decl.c_str()); + need_to_emit_cached_size = false; + } } // Field members: @@ -871,7 +1054,10 @@ GenerateClassDefinition(io::Printer* printer) { // For each oneof generate a union for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { printer->Print( - "union $camel_oneof_name$Union {\n", + "union $camel_oneof_name$Union {\n" + // explicit empty constructor is needed when union contains + // ArenaStringPtr members for string fields. + " $camel_oneof_name$Union() {}\n", "camel_oneof_name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true)); printer->Indent(); @@ -1029,10 +1215,28 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { " new ::google::protobuf::internal::GeneratedMessageReflection(\n" " $classname$_descriptor_,\n" " $classname$::default_instance_,\n" - " $classname$_offsets_,\n" - " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, _has_bits_[0]),\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"); + } + + // Unknown field offset: either points to the unknown field set if embedded + // directly, or indicates that the unknown field set is stored as part of the + // internal metadata if not. + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print(vars, + " -1,\n"); + } else { + printer->Print(vars, " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" "$classname$, _unknown_fields_),\n"); + } + if (descriptor_->extension_range_count() > 0) { printer->Print(vars, " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" @@ -1054,8 +1258,23 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { " ::google::protobuf::DescriptorPool::generated_pool(),\n"); printer->Print(vars, " ::google::protobuf::MessageFactory::generated_factory(),\n"); + printer->Print(vars, - " sizeof($classname$));\n"); + " sizeof($classname$),\n"); + + // Arena offset: either an offset to the metadata struct that contains the + // arena pointer and unknown field set (in a space-efficient way) if we use + // that implementation strategy, or an offset directly to the arena pointer if + // not (because e.g. we don't have an unknown field set). + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print(vars, + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" + "$classname$, _internal_metadata_));\n"); + } else { + printer->Print(vars, + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" + "$classname$, _arena_));\n"); + } // Handle nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { @@ -1100,7 +1319,7 @@ GenerateDefaultInstanceAllocator(io::Printer* printer) { if ((descriptor_->oneof_decl_count() > 0) && HasDescriptorMethods(descriptor_->file())) { printer->Print( - "$classname$_default_oneof_instance_ = new $classname$OneofInstance;\n", + "$classname$_default_oneof_instance_ = new $classname$OneofInstance();\n", "classname", classname_); } @@ -1312,8 +1531,10 @@ GenerateSharedConstructorCode(io::Printer* printer) { } } - printer->Print( - "::memset(_has_bits_, 0, sizeof(_has_bits_));\n"); + if (HasFieldPresence(descriptor_->file())) { + printer->Print( + "::memset(_has_bits_, 0, sizeof(_has_bits_));\n"); + } for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { printer->Print( @@ -1331,6 +1552,14 @@ GenerateSharedDestructorCode(io::Printer* printer) { "void $classname$::SharedDtor() {\n", "classname", classname_); printer->Indent(); + if (SupportsArenas(descriptor_)) { + // Do nothing when the message is allocated in an arena. + printer->Print( + "if (GetArenaNoVirtual() != NULL) {\n" + " return;\n" + "}\n" + "\n"); + } // Write the destructors for each field except oneof members. for (int i = 0; i < descriptor_->field_count(); i++) { if (!descriptor_->field(i)->containing_oneof()) { @@ -1381,19 +1610,107 @@ GenerateSharedDestructorCode(io::Printer* printer) { } void MessageGenerator:: +GenerateArenaDestructorCode(io::Printer* printer) { + // Generate the ArenaDtor() method. Track whether any fields actually produced + // code that needs to be called. + printer->Print( + "void $classname$::ArenaDtor(void* object) {\n", + "classname", classname_); + printer->Indent(); + + // This code is placed inside a static method, rather than an ordinary one, + // since that simplifies Arena's destructor list (ordinary function pointers + // rather than member function pointers). _this is the object being + // destructed. + printer->Print( + "$classname$* _this = reinterpret_cast< $classname$* >(object);\n" + // avoid an "unused variable" warning in case no fields have dtor code. + "(void)_this;\n", + "classname", classname_); + + bool need_registration = false; + for (int i = 0; i < descriptor_->field_count(); i++) { + if (field_generators_.get(descriptor_->field(i)) + .GenerateArenaDestructorCode(printer)) { + need_registration = true; + } + } + printer->Outdent(); + printer->Print( + "}\n"); + + if (need_registration) { + printer->Print( + "inline void $classname$::RegisterArenaDtor(::google::protobuf::Arena* arena) {\n" + " if (arena != NULL) {" + " arena->OwnCustomDestructor(this, &$classname$::ArenaDtor);\n" + " }\n" + "}\n", + "classname", classname_); + } else { + printer->Print( + "void $classname$::RegisterArenaDtor(::google::protobuf::Arena* arena) {\n" + "}\n", + "classname", classname_); + } +} + +void MessageGenerator:: GenerateStructors(io::Printer* printer) { string superclass = SuperClassName(descriptor_); + string initializer_with_arena; + if (UseUnknownFieldSet(descriptor_->file())) { + initializer_with_arena = "_internal_metadata_(arena)"; + } else { + initializer_with_arena = "_arena_ptr_(arena)"; + } + if (descriptor_->extension_range_count() > 0) { + initializer_with_arena = string("\n _extensions_(arena)") + + (!initializer_with_arena.empty() ? ", " : "") + initializer_with_arena; + } else { + initializer_with_arena = "\n " + initializer_with_arena; + } + + // Initialize member variables with arena constructor. + for (int i = 0; i < descriptor_->field_count(); i++) { + bool has_arena_constructor = descriptor_->field(i)->is_repeated(); + if (has_arena_constructor) { + initializer_with_arena += string(",\n ") + + FieldName(descriptor_->field(i)) + string("_(arena)"); + } + } + initializer_with_arena = superclass + "()" + + (!initializer_with_arena.empty() ? "," : " ") + initializer_with_arena; + + string initializer_null; + initializer_null = (UseUnknownFieldSet(descriptor_->file()) ? + ", _internal_metadata_(NULL) " : ", _arena_ptr_(NULL)"); - // Generate the default constructor. printer->Print( - "$classname$::$classname$()\n" - " : $superclass$() {\n" - " SharedCtor();\n" - " // @@protoc_insertion_point(constructor:$full_name$)\n" - "}\n", - "classname", classname_, - "superclass", superclass, - "full_name", descriptor_->full_name()); + "$classname$::$classname$()\n" + " : $superclass$() $initializer$ {\n" + " SharedCtor();\n" + " // @@protoc_insertion_point(constructor:$full_name$)\n" + "}\n", + "classname", classname_, + "superclass", superclass, + "full_name", descriptor_->full_name(), + "initializer", initializer_null); + + if (SupportsArenas(descriptor_)) { + printer->Print( + "\n" + "$classname$::$classname$(::google::protobuf::Arena* arena)\n" + " : $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()); + } printer->Print( "\n" @@ -1441,7 +1758,17 @@ GenerateStructors(io::Printer* printer) { // Generate the copy constructor. printer->Print( "$classname$::$classname$(const $classname$& from)\n" - " : $superclass$() {\n" + " : $superclass$()", + "classname", classname_, + "superclass", superclass, + "full_name", descriptor_->full_name()); + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + ",\n _internal_metadata_(NULL) {\n"); + } else if (!UseUnknownFieldSet(descriptor_->file())) { + printer->Print(",\n _arena_ptr_(NULL) {\n"); + } + printer->Print( " SharedCtor();\n" " MergeFrom(from);\n" " // @@protoc_insertion_point(copy_constructor:$full_name$)\n" @@ -1467,6 +1794,11 @@ GenerateStructors(io::Printer* printer) { // Generate the shared destructor code. GenerateSharedDestructorCode(printer); + // Generate the arena-specific destructor code. + if (SupportsArenas(descriptor_)) { + GenerateArenaDestructorCode(printer); + } + // Generate SetCachedSize. printer->Print( "void $classname$::SetCachedSize(int size) const {\n" @@ -1512,11 +1844,23 @@ GenerateStructors(io::Printer* printer) { "\n", "classname", classname_); - printer->Print( - "$classname$* $classname$::New() const {\n" - " return new $classname$;\n" - "}\n", - "classname", classname_); + if (SupportsArenas(descriptor_)) { + printer->Print( + "$classname$* $classname$::New(::google::protobuf::Arena* arena) const {\n" + " return ::google::protobuf::Arena::CreateMessage<$classname$>(arena);\n" + "}\n", + "classname", classname_); + } else { + printer->Print( + "$classname$* $classname$::New(::google::protobuf::Arena* arena) const {\n" + " $classname$* n = new $classname$;\n" + " if (arena != NULL) {\n" + " arena->Own(n);\n" + " }\n" + " return n;\n" + "}\n", + "classname", classname_); + } } @@ -1616,16 +1960,19 @@ GenerateClear(io::Printer* printer) { const string& memsets = memsets_for_chunk[i / 8]; uint32 mask = fields_mask_for_chunk[i / 8]; int count = popcnt(mask); + GOOGLE_DCHECK_GE(count, 1); if (count == 1 || (count <= 4 && count == memset_field_count_for_chunk[i / 8])) { // No "if" here because the chunk is trivial. } else { - printer->Print( - "if (_has_bits_[$index$ / 32] & $mask$) {\n", - "index", SimpleItoa(i / 8 * 8), - "mask", SimpleItoa(mask)); - printer->Indent(); - chunk_block_in_progress = true; + if (HasFieldPresence(descriptor_->file())) { + printer->Print( + "if (_has_bits_[$index$ / 32] & $mask$) {\n", + "index", SimpleItoa(i / 8 * 8), + "mask", SimpleItoa(mask)); + printer->Indent(); + chunk_block_in_progress = true; + } } printer->Print(memsets.c_str()); } @@ -1639,14 +1986,18 @@ GenerateClear(io::Printer* printer) { field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE || field->cpp_type() == FieldDescriptor::CPPTYPE_STRING; - if (should_check_bit) { + bool have_enclosing_if = false; + if (should_check_bit && + // If no field presence, then always clear strings/messages as well. + HasFieldPresence(descriptor_->file())) { printer->Print("if (has_$name$()) {\n", "name", fieldname); printer->Indent(); + have_enclosing_if = true; } field_generators_.get(field).GenerateClearingCode(printer); - if (should_check_bit) { + if (have_enclosing_if) { printer->Outdent(); printer->Print("}\n"); } @@ -1678,16 +2029,22 @@ GenerateClear(io::Printer* printer) { "oneof_name", descriptor_->oneof_decl(i)->name()); } - // Step 5: Everything else. - printer->Print( - "::memset(_has_bits_, 0, sizeof(_has_bits_));\n"); - - if (UseUnknownFieldSet(descriptor_->file())) { + if (HasFieldPresence(descriptor_->file())) { + // Step 5: Everything else. printer->Print( - "mutable_unknown_fields()->Clear();\n"); - } else { - printer->Print( - "mutable_unknown_fields()->clear();\n"); + "::memset(_has_bits_, 0, sizeof(_has_bits_));\n"); + } + + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "if (_internal_metadata_.have_unknown_fields()) {\n" + " mutable_unknown_fields()->Clear();\n" + "}\n"); + } else { + printer->Print( + "mutable_unknown_fields()->clear();\n"); + } } printer->Outdent(); @@ -1748,12 +2105,42 @@ GenerateOneofClear(io::Printer* printer) { void MessageGenerator:: GenerateSwap(io::Printer* printer) { - // Generate the Swap member function. - printer->Print("void $classname$::Swap($classname$* other) {\n", + if (SupportsArenas(descriptor_)) { + // Generate the Swap member function. This is a lightweight wrapper around + // UnsafeArenaSwap() / MergeFrom() with temporaries, depending on the memory + // ownership situation: swapping across arenas or between an arena and a + // heap requires copying. + printer->Print( + "void $classname$::Swap($classname$* other) {\n" + " if (other == this) return;\n" + " if (GetArenaNoVirtual() == other->GetArenaNoVirtual()) {\n" + " InternalSwap(other);\n" + " } else {\n" + " $classname$ temp;\n" + " temp.MergeFrom(*this);\n" + " CopyFrom(*other);\n" + " other->CopyFrom(temp);\n" + " }\n" + "}\n" + "void $classname$::UnsafeArenaSwap($classname$* other) {\n" + " if (other == this) return;\n" + " GOOGLE_DCHECK(GetArenaNoVirtual() == other->GetArenaNoVirtual());\n" + " InternalSwap(other);\n" + "}\n", + "classname", classname_); + } else { + printer->Print( + "void $classname$::Swap($classname$* other) {\n" + " if (other == this) return;\n" + " InternalSwap(other);\n" + "}\n", + "classname", classname_); + } + + // Generate the UnsafeArenaSwap member function. + printer->Print("void $classname$::InternalSwap($classname$* other) {\n", "classname", classname_); printer->Indent(); - printer->Print("if (other != this) {\n"); - printer->Indent(); if (HasGeneratedMethods(descriptor_->file())) { for (int i = 0; i < descriptor_->field_count(); i++) { @@ -1769,15 +2156,25 @@ GenerateSwap(io::Printer* printer) { "i", SimpleItoa(i)); } - for (int i = 0; i < (descriptor_->field_count() + 31) / 32; ++i) { - printer->Print("std::swap(_has_bits_[$i$], other->_has_bits_[$i$]);\n", - "i", SimpleItoa(i)); + if (HasFieldPresence(descriptor_->file())) { + for (int i = 0; i < (descriptor_->field_count() + 31) / 32; ++i) { + printer->Print("std::swap(_has_bits_[$i$], other->_has_bits_[$i$]);\n", + "i", SimpleItoa(i)); + } } - if (UseUnknownFieldSet(descriptor_->file())) { - printer->Print("_unknown_fields_.Swap(&other->_unknown_fields_);\n"); + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "_internal_metadata_.Swap(&other->_internal_metadata_);\n"); + } else { + printer->Print("_unknown_fields_.swap(other->_unknown_fields_);\n"); + } } else { - printer->Print("_unknown_fields_.swap(other->_unknown_fields_);\n"); + // Still swap internal_metadata as it may contain more than just + // unknown fields. + printer->Print( + "_internal_metadata_.Swap(&other->_internal_metadata_);\n"); } printer->Print("std::swap(_cached_size_, other->_cached_size_);\n"); if (descriptor_->extension_range_count() > 0) { @@ -1789,8 +2186,6 @@ GenerateSwap(io::Printer* printer) { printer->Outdent(); printer->Print("}\n"); - printer->Outdent(); - printer->Print("}\n"); } void MessageGenerator:: @@ -1886,33 +2281,48 @@ GenerateMergeFrom(io::Printer* printer) { const FieldDescriptor* field = descriptor_->field(i); if (!field->is_repeated() && !field->containing_oneof()) { - // See above in GenerateClear for an explanation of this. - if (i / 8 != last_index / 8 || last_index < 0) { - if (last_index >= 0) { - printer->Outdent(); - printer->Print("}\n"); + if (HasFieldPresence(descriptor_->file())) { + // See above in GenerateClear for an explanation of this. + if (i / 8 != last_index / 8 || last_index < 0) { + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + printer->Print( + "if (from._has_bits_[$index$ / 32] & " + "(0xffu << ($index$ % 32))) {\n", + "index", SimpleItoa(field->index())); + printer->Indent(); } - printer->Print( - "if (from._has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n", - "index", SimpleItoa(field->index())); - printer->Indent(); } last_index = i; - printer->Print( - "if (from.has_$name$()) {\n", - "name", FieldName(field)); - printer->Indent(); + bool have_enclosing_if = false; + if (HasFieldPresence(descriptor_->file())) { + printer->Print( + "if (from.has_$name$()) {\n", + "name", FieldName(field)); + printer->Indent(); + have_enclosing_if = true; + } else { + // Merge semantics without true field presence: primitive fields are + // merged only if non-zero (numeric) or non-empty (string). + have_enclosing_if = EmitFieldNonDefaultCondition( + printer, "from.", field); + } field_generators_.get(field).GenerateMergingCode(printer); - printer->Outdent(); - printer->Print("}\n"); + if (have_enclosing_if) { + printer->Outdent(); + printer->Print("}\n"); + } } } - if (last_index >= 0) { + if (HasFieldPresence(descriptor_->file()) && + last_index >= 0) { printer->Outdent(); printer->Print("}\n"); } @@ -1921,12 +2331,16 @@ GenerateMergeFrom(io::Printer* printer) { printer->Print("_extensions_.MergeFrom(from._extensions_);\n"); } - if (UseUnknownFieldSet(descriptor_->file())) { - printer->Print( - "mutable_unknown_fields()->MergeFrom(from.unknown_fields());\n"); - } else { - printer->Print( - "mutable_unknown_fields()->append(from.unknown_fields());\n"); + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "if (from._internal_metadata_.have_unknown_fields()) {\n" + " mutable_unknown_fields()->MergeFrom(from.unknown_fields());\n" + "}\n"); + } else { + printer->Print( + "mutable_unknown_fields()->append(from.unknown_fields());\n"); + } } printer->Outdent(); @@ -2171,24 +2585,33 @@ GenerateMergeFromCodedStream(io::Printer* printer) { } } printer->Print(") {\n"); - if (UseUnknownFieldSet(descriptor_->file())) { - PrintHandlingOptionalStaticInitializers( - descriptor_->file(), 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" - " mutable_unknown_fields()));\n"); + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + PrintHandlingOptionalStaticInitializers( + descriptor_->file(), 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" + " mutable_unknown_fields()));\n"); + } else { + PrintHandlingOptionalStaticInitializers( + descriptor_->file(), 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" + " &unknown_fields_stream));\n"); + } } else { PrintHandlingOptionalStaticInitializers( descriptor_->file(), printer, // With static initializers. - " DO_(_extensions_.ParseField(tag, input, default_instance_,\n" - " &unknown_fields_stream));\n", + " DO_(_extensions_.ParseField(tag, input, default_instance_);\n", // Without. - " DO_(_extensions_.ParseField(tag, input, &default_instance(),\n" - " &unknown_fields_stream));\n"); + " DO_(_extensions_.ParseField(tag, input, &default_instance());\n"); } printer->Print( " continue;\n" @@ -2196,14 +2619,19 @@ GenerateMergeFromCodedStream(io::Printer* printer) { } // We really don't recognize this tag. Skip it. - if (UseUnknownFieldSet(descriptor_->file())) { - printer->Print( - "DO_(::google::protobuf::internal::WireFormat::SkipField(\n" - " input, tag, mutable_unknown_fields()));\n"); + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "DO_(::google::protobuf::internal::WireFormat::SkipField(\n" + " input, tag, mutable_unknown_fields()));\n"); + } else { + printer->Print( + "DO_(::google::protobuf::internal::WireFormatLite::SkipField(\n" + " input, tag, &unknown_fields_stream));\n"); + } } else { printer->Print( - "DO_(::google::protobuf::internal::WireFormatLite::SkipField(\n" - " input, tag, &unknown_fields_stream));\n"); + "DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));\n"); } if (descriptor_->field_count() > 0) { @@ -2232,11 +2660,15 @@ void MessageGenerator::GenerateSerializeOneField( io::Printer* printer, const FieldDescriptor* field, bool to_array) { PrintFieldComment(printer, field); - if (!field->is_repeated()) { + bool have_enclosing_if = false; + if (!field->is_repeated() && HasFieldPresence(descriptor_->file())) { printer->Print( "if (has_$name$()) {\n", "name", FieldName(field)); printer->Indent(); + have_enclosing_if = true; + } else if (!HasFieldPresence(descriptor_->file())) { + have_enclosing_if = EmitFieldNonDefaultCondition(printer, "this->", field); } if (to_array) { @@ -2246,7 +2678,7 @@ void MessageGenerator::GenerateSerializeOneField( field_generators_.get(field).GenerateSerializeWithCachedSizes(printer); } - if (!field->is_repeated()) { + if (have_enclosing_if) { printer->Outdent(); printer->Print("}\n"); } @@ -2386,28 +2818,69 @@ GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) { } } - if (UseUnknownFieldSet(descriptor_->file())) { - printer->Print("if (!unknown_fields().empty()) {\n"); - printer->Indent(); - if (to_array) { + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print("if (_internal_metadata_.have_unknown_fields()) {\n"); + printer->Indent(); + if (to_array) { + printer->Print( + "target = " + "::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(\n" + " unknown_fields(), target);\n"); + } else { + printer->Print( + "::google::protobuf::internal::WireFormat::SerializeUnknownFields(\n" + " unknown_fields(), output);\n"); + } + printer->Outdent(); + printer->Print( - "target = " - "::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(\n" - " unknown_fields(), target);\n"); + "}\n"); } else { printer->Print( - "::google::protobuf::internal::WireFormat::SerializeUnknownFields(\n" - " unknown_fields(), output);\n"); + "output->WriteRaw(unknown_fields().data(),\n" + " unknown_fields().size());\n"); } - printer->Outdent(); + } +} - printer->Print( - "}\n"); - } else { - printer->Print( - "output->WriteRaw(unknown_fields().data(),\n" - " unknown_fields().size());\n"); +static vector<uint32> RequiredFieldsBitMask(const Descriptor* desc) { + vector<uint32> result; + uint32 mask = 0; + for (int i = 0; i < desc->field_count(); i++) { + if (i > 0 && i % 32 == 0) { + result.push_back(mask); + mask = 0; + } + if (desc->field(i)->is_required()) { + mask |= (1 << (i & 31)); + } + } + if (mask != 0) { + result.push_back(mask); } + return result; +} + +// Create an expression that evaluates to +// "for all i, (_has_bits_[i] & masks[i]) == masks[i]" +// masks is allowed to be shorter than _has_bits_, but at least one element of +// masks must be non-zero. +static string ConditionalToCheckBitmasks(const vector<uint32>& masks) { + vector<string> parts; + for (int i = 0; i < masks.size(); i++) { + if (masks[i] == 0) continue; + char buffer[kFastToBufferSize]; + FastHex32ToBuffer(masks[i], buffer); + string m = StrCat("0x", buffer); + // Each xor evaluates to 0 if the expected bits are present. + parts.push_back(StrCat("((_has_bits_[", i, "] & ", m, ") ^ ", m, ")")); + } + GOOGLE_CHECK(!parts.empty()); + // If we have multiple parts, each expected to be 0, then bitwise-or them. + string result = parts.size() == 1 ? parts[0] : + StrCat("(", Join(parts, "\n | "), ")"); + return result + " == 0"; } void MessageGenerator:: @@ -2420,8 +2893,10 @@ GenerateByteSize(io::Printer* printer) { "classname", classname_); GOOGLE_CHECK(UseUnknownFieldSet(descriptor_->file())); printer->Print( + "if (_internal_metadata_.have_unknown_fields()) {\n" " total_size += ::google::protobuf::internal::WireFormat::\n" - " ComputeUnknownMessageSetItemsSize(unknown_fields());\n"); + " ComputeUnknownMessageSetItemsSize(unknown_fields());\n" + "}\n"); printer->Print( " GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();\n" " _cached_size_ = total_size;\n" @@ -2431,6 +2906,33 @@ GenerateByteSize(io::Printer* printer) { return; } + if (num_required_fields_ > 1 && HasFieldPresence(descriptor_->file())) { + // 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", + "classname", classname_); + printer->Indent(); + printer->Print("int total_size = 0;\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (field->is_required()) { + printer->Print("\n" + "if (has_$name$()) {\n", + "name", FieldName(field)); + printer->Indent(); + PrintFieldComment(printer, field); + field_generators_.get(field).GenerateByteSize(printer); + printer->Outdent(); + printer->Print("}\n"); + } + } + printer->Print("\n" + "return total_size;\n"); + printer->Outdent(); + printer->Print("}\n"); + } + printer->Print( "int $classname$::ByteSize() const {\n", "classname", classname_); @@ -2439,45 +2941,121 @@ GenerateByteSize(io::Printer* printer) { "int total_size = 0;\n" "\n"); - int last_index = -1; + // Handle required fields (if any). We expect all of them to be + // present, so emit one conditional that checks for that. If they are all + // present then the fast path executes; otherwise the slow path executes. + if (num_required_fields_ > 1 && HasFieldPresence(descriptor_->file())) { + // The fast path works if all required fields are present. + vector<uint32> masks_for_has_bits = RequiredFieldsBitMask(descriptor_); + printer->Print((string("if (") + + ConditionalToCheckBitmasks(masks_for_has_bits) + + ") { // All required fields are present.\n").c_str()); + printer->Indent(); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (!field->is_required()) continue; + PrintFieldComment(printer, field); + field_generators_.get(field).GenerateByteSize(printer); + printer->Print("\n"); + } + printer->Outdent(); + printer->Print("} else {\n" // the slow path + " total_size += RequiredFieldsByteSizeFallback();\n" + "}\n"); + } else { + // num_required_fields_ <= 1: no need to be tricky + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (!field->is_required()) continue; + PrintFieldComment(printer, field); + printer->Print("if (has_$name$()) {\n", + "name", FieldName(field)); + printer->Indent(); + field_generators_.get(field).GenerateByteSize(printer); + printer->Outdent(); + printer->Print("}\n"); + } + } + // Handle optional fields (worry below about repeateds, oneofs, etc.). + // These are handled in chunks of 8. The first chunk is + // the non-requireds-non-repeateds-non-unions-non-extensions in + // descriptor_->field(0), descriptor_->field(1), ... descriptor_->field(7), + // and the second chunk is the same for + // descriptor_->field(8), descriptor_->field(9), ... descriptor_->field(15), + // etc. + hash_map<int, uint32> fields_mask_for_chunk; for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); + if (!field->is_required() && !field->is_repeated() && + !field->containing_oneof()) { + fields_mask_for_chunk[i / 8] |= static_cast<uint32>(1) << (i % 32); + } + } - if (!field->is_repeated() && !field->containing_oneof()) { + int last_index = -1; + bool chunk_block_in_progress = false; + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (!field->is_required() && !field->is_repeated() && + !field->containing_oneof()) { // See above in GenerateClear for an explanation of this. // TODO(kenton): Share code? Unclear how to do so without // over-engineering. - if ((i / 8) != (last_index / 8) || - last_index < 0) { - if (last_index >= 0) { + if (i / 8 != last_index / 8 || last_index < 0) { + // End previous chunk, if there was one. + if (chunk_block_in_progress) { printer->Outdent(); printer->Print("}\n"); + chunk_block_in_progress = false; + } + // Start chunk. + uint32 mask = fields_mask_for_chunk[i / 8]; + int count = popcnt(mask); + GOOGLE_DCHECK_GE(count, 1); + if (count == 1) { + // No "if" here because the chunk is trivial. + } else { + if (HasFieldPresence(descriptor_->file())) { + printer->Print( + "if (_has_bits_[$index$ / 32] & $mask$) {\n", + "index", SimpleItoa(i), + "mask", SimpleItoa(mask)); + printer->Indent(); + chunk_block_in_progress = true; + } } - printer->Print( - "if (_has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n", - "index", SimpleItoa(field->index())); - printer->Indent(); } last_index = i; PrintFieldComment(printer, field); - printer->Print( - "if (has_$name$()) {\n", - "name", FieldName(field)); - printer->Indent(); + bool have_enclosing_if = false; + if (HasFieldPresence(descriptor_->file())) { + printer->Print( + "if (has_$name$()) {\n", + "name", FieldName(field)); + printer->Indent(); + have_enclosing_if = true; + } else { + // Without field presence: field is serialized only if it has a + // non-default value. + have_enclosing_if = EmitFieldNonDefaultCondition( + printer, "this->", field); + } field_generators_.get(field).GenerateByteSize(printer); - printer->Outdent(); - printer->Print( - "}\n" - "\n"); + if (have_enclosing_if) { + printer->Outdent(); + printer->Print( + "}\n" + "\n"); + } } } - if (last_index >= 0) { + if (chunk_block_in_progress) { printer->Outdent(); printer->Print("}\n"); } @@ -2532,19 +3110,19 @@ GenerateByteSize(io::Printer* printer) { "\n"); } - if (UseUnknownFieldSet(descriptor_->file())) { - printer->Print("if (!unknown_fields().empty()) {\n"); - printer->Indent(); - printer->Print( - "total_size +=\n" - " ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(\n" - " unknown_fields());\n"); - printer->Outdent(); - printer->Print("}\n"); - } else { - printer->Print( - "total_size += unknown_fields().size();\n" - "\n"); + if (PreserveUnknownFields(descriptor_)) { + if (UseUnknownFieldSet(descriptor_->file())) { + printer->Print( + "if (_internal_metadata_.have_unknown_fields()) {\n" + " total_size +=\n" + " ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(\n" + " unknown_fields());\n" + "}\n"); + } else { + printer->Print( + "total_size += unknown_fields().size();\n" + "\n"); + } } // We update _cached_size_ even though this is a const method. In theory, @@ -2569,27 +3147,29 @@ GenerateIsInitialized(io::Printer* printer) { "classname", classname_); printer->Indent(); - // Check that all required fields in this message are set. We can do this - // most efficiently by checking 32 "has bits" at a time. - int has_bits_array_size = (descriptor_->field_count() + 31) / 32; - for (int i = 0; i < has_bits_array_size; i++) { - uint32 mask = 0; - for (int bit = 0; bit < 32; bit++) { - int index = i * 32 + bit; - if (index >= descriptor_->field_count()) break; - const FieldDescriptor* field = descriptor_->field(index); - - if (field->is_required()) { - mask |= 1 << bit; + if (HasFieldPresence(descriptor_->file())) { + // Check that all required fields in this message are set. We can do this + // most efficiently by checking 32 "has bits" at a time. + int has_bits_array_size = (descriptor_->field_count() + 31) / 32; + for (int i = 0; i < has_bits_array_size; i++) { + uint32 mask = 0; + for (int bit = 0; bit < 32; bit++) { + int index = i * 32 + bit; + if (index >= descriptor_->field_count()) break; + const FieldDescriptor* field = descriptor_->field(index); + + if (field->is_required()) { + mask |= 1 << bit; + } } - } - if (mask != 0) { - char buffer[kFastToBufferSize]; - printer->Print( - "if ((_has_bits_[$i$] & 0x$mask$) != 0x$mask$) return false;\n", - "i", SimpleItoa(i), - "mask", FastHex32ToBuffer(mask, buffer)); + if (mask != 0) { + char buffer[kFastToBufferSize]; + printer->Print( + "if ((_has_bits_[$i$] & 0x$mask$) != 0x$mask$) return false;\n", + "i", SimpleItoa(i), + "mask", FastHex32ToBuffer(mask, buffer)); + } } } @@ -2607,7 +3187,7 @@ GenerateIsInitialized(io::Printer* printer) { "name", FieldName(field)); } else { if (field->options().weak()) { - // For weak fields, use the data member (google::protobuf::Message*) instead + // 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( |