diff options
Diffstat (limited to 'src')
39 files changed, 2551 insertions, 414 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index c13a723e..18167482 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -160,6 +160,7 @@ EXTRA_DIST = \ $(protoc_inputs) \ solaris/libstdc++.la \ google/protobuf/testdata/golden_message \ + google/protobuf/testdata/golden_packed_fields_message \ google/protobuf/testdata/text_format_unittest_data.txt \ google/protobuf/testdata/text_format_unittest_extensions_data.txt \ google/protobuf/package_info.h \ diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index 089844f4..09473ec7 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -751,7 +751,8 @@ bool CommandLineInterface::GenerateOutput( if (!output_directive.generator->Generate( parsed_file, output_directive.parameter, &output_directory, &error)) { // Generator returned an error. - cerr << output_directive.name << ": " << error << endl; + cerr << parsed_file->name() << ": " << output_directive.name << ": " + << error << endl; return false; } diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc index c998f20b..19779a8a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc @@ -158,7 +158,13 @@ RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {} void RepeatedEnumFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { - printer->Print(variables_, "::google::protobuf::RepeatedField<int> $name$_;\n"); + printer->Print(variables_, + "::google::protobuf::RepeatedField<int> $name$_;\n"); + if (descriptor_->options().packed() && + descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + printer->Print(variables_, + "mutable int _$name$_cached_byte_size_;\n"); + } } void RepeatedEnumFieldGenerator:: @@ -217,31 +223,84 @@ GenerateInitializer(io::Printer* printer) const { void RepeatedEnumFieldGenerator:: GenerateMergeFromCodedStream(io::Printer* printer) const { - printer->Print(variables_, - "int value;\n" - "DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value));\n" - "if ($type$_IsValid(value)) {\n" - " add_$name$(static_cast< $type$ >(value));\n" - "} else {\n" - " mutable_unknown_fields()->AddField($number$)->add_varint(value);\n" - "}\n"); + if (descriptor_->options().packed()) { + printer->Print(variables_, + "::google::protobuf::uint32 length;\n" + "DO_(input->ReadVarint32(&length));\n" + "::google::protobuf::io::CodedInputStream::Limit limit = " + "input->PushLimit(length);\n" + "while (input->BytesUntilLimit() > 0) {\n" + " int value;\n" + " DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value));\n" + " if ($type$_IsValid(value)) {\n" + " add_$name$(static_cast< $type$ >(value));\n" + " }\n" + "}\n" + "input->PopLimit(limit);\n"); + } else { + printer->Print(variables_, + "int value;\n" + "DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value));\n" + "if ($type$_IsValid(value)) {\n" + " add_$name$(static_cast< $type$ >(value));\n" + "} else {\n" + " mutable_unknown_fields()->AddField($number$)->add_varint(value);\n" + "}\n"); + } } void RepeatedEnumFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { + if (descriptor_->options().packed()) { + // Write the tag and the size. + printer->Print(variables_, + "if (this->$name$_size() > 0) {\n" + " DO_(::google::protobuf::internal::WireFormat::WriteTag(" + "$number$, ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED," + "output));\n" + " DO_(output->WriteVarint32(_$name$_cached_byte_size_));\n" + "}\n"); + } printer->Print(variables_, - "DO_(::google::protobuf::internal::WireFormat::WriteEnum(" - "$number$, this->$name$(i), output));\n"); + "for (int i = 0; i < this->$name$_size(); i++) {\n"); + if (descriptor_->options().packed()) { + printer->Print(variables_, + " DO_(::google::protobuf::internal::WireFormat::WriteEnumNoTag(" + "this->$name$(i), output));\n"); + } else { + printer->Print(variables_, + " DO_(::google::protobuf::internal::WireFormat::WriteEnum(" + "$number$, this->$name$(i), output));\n"); + } + printer->Print("}\n"); } void RepeatedEnumFieldGenerator:: GenerateByteSize(io::Printer* printer) const { printer->Print(variables_, - "total_size += $tag_size$ * $name$_size();\n" - "for (int i = 0; i < $name$_size(); i++) {\n" - " total_size += ::google::protobuf::internal::WireFormat::EnumSize(\n" - " this->$name$(i));\n" - "}\n"); + "{\n" + " int data_size = 0;\n"); + printer->Indent(); + printer->Print(variables_, + "for (int i = 0; i < this->$name$_size(); i++) {\n" + " data_size += ::google::protobuf::internal::WireFormat::EnumSize(\n" + " this->$name$(i));\n" + "}\n"); + + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (data_size > 0) {\n" + " total_size += $tag_size$ + " + "::google::protobuf::internal::WireFormat::Int32Size(data_size);\n" + "}\n" + "_$name$_cached_byte_size_ = data_size;\n" + "total_size += data_size;\n"); + } else { + printer->Print(variables_, + "total_size += $tag_size$ * this->$name$_size() + data_size;\n"); + } + printer->Outdent(); + printer->Print("}\n"); } } // namespace cpp diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index eacceeaf..c6843e93 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -41,7 +41,7 @@ #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/wire_format_inl.h> #include <google/protobuf/descriptor.pb.h> namespace google { @@ -1169,10 +1169,9 @@ GenerateMergeFromCodedStream(io::Printer* printer) { " goto handle_uninterpreted;\n" " }\n", "number", SimpleItoa(field->number()), - "wiretype", kWireTypeNames[ - WireFormat::WireTypeForFieldType(field->type())]); + "wiretype", kWireTypeNames[WireFormat::WireTypeForField(field)]); - if (i > 0 || field->is_repeated()) { + if (i > 0 || (field->is_repeated() && !field->options().packed())) { printer->Print( " parse_$name$:\n", "name", field->name()); @@ -1184,7 +1183,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) { // switch() is slow since it can't be predicted well. Insert some if()s // here that attempt to predict the next tag. - if (field->is_repeated()) { + if (field->is_repeated() && !field->options().packed()) { // Expect repeats of this field. printer->Print( "if (input->ExpectTag($tag$)) goto parse_$name$;\n", @@ -1283,22 +1282,20 @@ void MessageGenerator::GenerateSerializeOneField( io::Printer* printer, const FieldDescriptor* field) { PrintFieldComment(printer, field); - if (field->is_repeated()) { - printer->Print( - "for (int i = 0; i < $name$_.size(); i++) {\n", - "name", FieldName(field)); - } else { + if (!field->is_repeated()) { printer->Print( "if (_has_bit($index$)) {\n", "index", SimpleItoa(field->index())); + printer->Indent(); } - printer->Indent(); - field_generators_.get(field).GenerateSerializeWithCachedSizes(printer); - printer->Outdent(); - printer->Print("}\n\n"); + if (!field->is_repeated()) { + printer->Outdent(); + printer->Print("}\n"); + } + printer->Print("\n"); } void MessageGenerator::GenerateSerializeOneExtensionRange( diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.cc b/src/google/protobuf/compiler/cpp/cpp_message_field.cc index d1c31067..7d57a6df 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc @@ -232,15 +232,17 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { void RepeatedMessageFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { printer->Print(variables_, - "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$NoVirtual(" - "$number$, this->$name$(i), output));\n"); + "for (int i = 0; i < this->$name$_size(); i++) {\n" + " DO_(::google::protobuf::internal::WireFormat::Write$declared_type$NoVirtual(" + "$number$, this->$name$(i), output));\n" + "}\n"); } void RepeatedMessageFieldGenerator:: GenerateByteSize(io::Printer* printer) const { printer->Print(variables_, - "total_size += $tag_size$ * $name$_size();\n" - "for (int i = 0; i < $name$_size(); i++) {\n" + "total_size += $tag_size$ * this->$name$_size();\n" + "for (int i = 0; i < this->$name$_size(); i++) {\n" " total_size +=\n" " ::google::protobuf::internal::WireFormat::$declared_type$SizeNoVirtual(\n" " this->$name$(i));\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc index ef4072f0..768d30cc 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc @@ -227,6 +227,11 @@ void RepeatedPrimitiveFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { printer->Print(variables_, "::google::protobuf::RepeatedField< $type$ > $name$_;\n"); + if (descriptor_->options().packed() && + descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + printer->Print(variables_, + "mutable int _$name$_cached_byte_size_;\n"); + } } void RepeatedPrimitiveFieldGenerator:: @@ -283,33 +288,90 @@ GenerateInitializer(io::Printer* printer) const { void RepeatedPrimitiveFieldGenerator:: GenerateMergeFromCodedStream(io::Printer* printer) const { - printer->Print(variables_, - "$type$ value;\n" - "DO_(::google::protobuf::internal::WireFormat::Read$declared_type$(input, &value));\n" - "add_$name$(value);\n"); + if (descriptor_->options().packed()) { + printer->Print("{\n"); + printer->Indent(); + printer->Print(variables_, + "::google::protobuf::uint32 length;\n" + "DO_(input->ReadVarint32(&length));\n" + "::google::protobuf::io::CodedInputStream::Limit limit = " + "input->PushLimit(length);\n" + "while (input->BytesUntilLimit() > 0) {\n" + " $type$ value;\n" + " DO_(::google::protobuf::internal::WireFormat::Read$declared_type$(" + "input, &value));\n" + " add_$name$(value);\n" + "}\n" + "input->PopLimit(limit);\n"); + printer->Outdent(); + printer->Print("}\n"); + } else { + printer->Print(variables_, + "$type$ value;\n" + "DO_(::google::protobuf::internal::WireFormat::Read$declared_type$(" + "input, &value));\n" + "add_$name$(value);\n"); + } } void RepeatedPrimitiveFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { + if (descriptor_->options().packed()) { + // Write the tag and the size. + printer->Print(variables_, + "if (this->$name$_size() > 0) {\n" + " DO_(::google::protobuf::internal::WireFormat::WriteTag(" + "$number$, ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED," + "output));\n" + " DO_(output->WriteVarint32(_$name$_cached_byte_size_));\n" + "}\n"); + } printer->Print(variables_, - "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" - "$number$, this->$name$(i), output));\n"); + "for (int i = 0; i < this->$name$_size(); i++) {\n"); + if (descriptor_->options().packed()) { + printer->Print(variables_, + " DO_(::google::protobuf::internal::WireFormat::Write$declared_type$NoTag(" + "this->$name$(i), output));\n"); + } else { + printer->Print(variables_, + " DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(i), output));\n"); + } + printer->Print("}\n"); } void RepeatedPrimitiveFieldGenerator:: GenerateByteSize(io::Printer* printer) const { + printer->Print(variables_, + "{\n" + " int data_size = 0;\n"); + printer->Indent(); int fixed_size = FixedSize(descriptor_->type()); if (fixed_size == -1) { printer->Print(variables_, - "total_size += $tag_size$ * $name$_size();\n" - "for (int i = 0; i < $name$_size(); i++) {\n" - " total_size += ::google::protobuf::internal::WireFormat::$declared_type$Size(\n" + "for (int i = 0; i < this->$name$_size(); i++) {\n" + " data_size += ::google::protobuf::internal::WireFormat::$declared_type$Size(\n" " this->$name$(i));\n" "}\n"); } else { printer->Print(variables_, - "total_size += ($tag_size$ + $fixed_size$) * $name$_size();\n"); + "data_size = $fixed_size$ * this->$name$_size();\n"); + } + + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (data_size > 0) {\n" + " total_size += $tag_size$ + " + "::google::protobuf::internal::WireFormat::Int32Size(data_size);\n" + "}\n" + "_$name$_cached_byte_size_ = data_size;\n" + "total_size += data_size;\n"); + } else { + printer->Print(variables_, + "total_size += $tag_size$ * this->$name$_size() + data_size;\n"); } + printer->Outdent(); + printer->Print("}\n"); } } // namespace cpp diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc index 3e694ab7..200e3d68 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -374,15 +374,17 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { void RepeatedStringFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { printer->Print(variables_, - "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" - "$number$, this->$name$(i), output));\n"); + "for (int i = 0; i < this->$name$_size(); i++) {\n" + " DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(i), output));\n" + "}\n"); } void RepeatedStringFieldGenerator:: GenerateByteSize(io::Printer* printer) const { printer->Print(variables_, - "total_size += $tag_size$ * $name$_size();\n" - "for (int i = 0; i < $name$_size(); i++) {\n" + "total_size += $tag_size$ * this->$name$_size();\n" + "for (int i = 0; i < this->$name$_size(); i++) {\n" " total_size += ::google::protobuf::internal::WireFormat::$declared_type$Size(\n" " this->$name$(i));\n" "}\n"); diff --git a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto index a4d96ac5..79971a95 100644 --- a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto +++ b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto @@ -54,6 +54,14 @@ message TestConflictingSymbolNames { optional int32 total_size = 6; optional int32 tag = 7; + enum TestEnum { FOO = 1; } + message Data1 { repeated int32 data = 1; } + message Data2 { repeated TestEnum data = 1; } + message Data3 { repeated string data = 1; } + message Data4 { repeated Data4 data = 1; } + message Data5 { repeated string data = 1 [ctype=STRING_PIECE]; } + message Data6 { repeated string data = 1 [ctype=CORD]; } + optional int32 source = 8; optional int32 value = 9; optional int32 file = 10; diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc index 393c923b..c7e4ee3d 100644 --- a/src/google/protobuf/compiler/cpp/cpp_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -225,7 +225,6 @@ TEST(GeneratedMessageTest, ClearOneField) { TEST(GeneratedMessageTest, CopyFrom) { unittest::TestAllTypes message1, message2; - string data; TestUtil::SetAllFields(&message1); message2.CopyFrom(message1); @@ -413,6 +412,13 @@ TEST(GeneratedMessageTest, Serialization) { EXPECT_TRUE(message2.ParseFromString(data)); TestUtil::ExpectAllFieldsSet(message2); + + unittest::TestPackedTypes packed_message1, packed_message2; + string packed_data; + TestUtil::SetPackedFields(&packed_message1); + packed_message1.SerializeToString(&packed_data); + EXPECT_TRUE(packed_message2.ParseFromString(packed_data)); + TestUtil::ExpectPackedFieldsSet(packed_message2); } diff --git a/src/google/protobuf/compiler/java/java_enum.cc b/src/google/protobuf/compiler/java/java_enum.cc index b1484763..4aac6493 100644 --- a/src/google/protobuf/compiler/java/java_enum.cc +++ b/src/google/protobuf/compiler/java/java_enum.cc @@ -71,7 +71,8 @@ void EnumGenerator::Generate(io::Printer* printer) { descriptor_->containing_type() == NULL && descriptor_->file()->options().java_multiple_files(); printer->Print( - "public $static$ enum $classname$ {\n", + "public $static$ enum $classname$\n" + " implements com.google.protobuf.ProtocolMessageEnum {\n", "static", is_own_file ? "" : "static", "classname", descriptor_->name()); printer->Indent(); diff --git a/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc index e95fdab4..2153042d 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.cc +++ b/src/google/protobuf/compiler/java/java_enum_field.cc @@ -39,6 +39,7 @@ #include <google/protobuf/stubs/common.h> #include <google/protobuf/compiler/java/java_helpers.h> #include <google/protobuf/io/printer.h> +#include <google/protobuf/wire_format_inl.h> #include <google/protobuf/stubs/strutil.h> namespace google { @@ -64,6 +65,9 @@ void SetEnumVariables(const FieldDescriptor* descriptor, (*variables)["number"] = SimpleItoa(descriptor->number()); (*variables)["type"] = type; (*variables)["default"] = type + "." + default_value->name(); + (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); + (*variables)["tag_size"] = SimpleItoa( + internal::WireFormat::TagSize(descriptor->number(), descriptor->type())); } } // namespace @@ -97,6 +101,9 @@ GenerateBuilderMembers(io::Printer* printer) const { " return result.get$capitalized_name$();\n" "}\n" "public Builder set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" " result.has$capitalized_name$ = true;\n" " result.$name$_ = value;\n" " return this;\n" @@ -176,6 +183,12 @@ GenerateMembers(io::Printer* printer) const { "public $type$ get$capitalized_name$(int index) {\n" " return $name$_.get(index);\n" "}\n"); + + if (descriptor_->options().packed() && + descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + printer->Print(variables_, + "private int $name$MemoizedSerializedSize;\n"); + } } void RepeatedEnumFieldGenerator:: @@ -195,10 +208,16 @@ GenerateBuilderMembers(io::Printer* printer) const { " return result.get$capitalized_name$(index);\n" "}\n" "public Builder set$capitalized_name$(int index, $type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" " result.$name$_.set(index, value);\n" " return this;\n" "}\n" "public Builder add$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" " if (result.$name$_.isEmpty()) {\n" " result.$name$_ = new java.util.ArrayList<$type$>();\n" " }\n" @@ -241,6 +260,16 @@ GenerateBuildingCode(io::Printer* printer) const { void RepeatedEnumFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { + // If packed, set up the while loop + if (descriptor_->options().packed()) { + printer->Print(variables_, + "int length = input.readRawVarint32();\n" + "int oldLimit = input.pushLimit(length);\n" + "while(input.getBytesUntilLimit() > 0) {\n"); + printer->Indent(); + } + + // Read and store the enum printer->Print(variables_, "int rawValue = input.readEnum();\n" "$type$ value = $type$.valueOf(rawValue);\n" @@ -249,23 +278,68 @@ GenerateParsingCode(io::Printer* printer) const { "} else {\n" " add$capitalized_name$(value);\n" "}\n"); + + if (descriptor_->options().packed()) { + printer->Outdent(); + printer->Print(variables_, + "}\n" + "input.popLimit(oldLimit);\n"); + } } void RepeatedEnumFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { - printer->Print(variables_, - "for ($type$ element : get$capitalized_name$List()) {\n" - " output.writeEnum($number$, element.getNumber());\n" - "}\n"); + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (get$capitalized_name$List().size() > 0) {\n" + " output.writeRawVarint32($tag$);\n" + " output.writeRawVarint32($name$MemoizedSerializedSize);\n" + "}\n" + "for ($type$ element : get$capitalized_name$List()) {\n" + " output.writeEnumNoTag(element.getNumber());\n" + "}\n"); + } else { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " output.writeEnum($number$, element.getNumber());\n" + "}\n"); + } } void RepeatedEnumFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, + "{\n" + " int dataSize = 0;\n"); + printer->Indent(); + + printer->Print(variables_, "for ($type$ element : get$capitalized_name$List()) {\n" - " size += com.google.protobuf.CodedOutputStream\n" - " .computeEnumSize($number$, element.getNumber());\n" + " dataSize += com.google.protobuf.CodedOutputStream\n" + " .computeEnumSizeNoTag(element.getNumber());\n" "}\n"); + printer->Print( + "size += dataSize;\n"); + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (!get$capitalized_name$List().isEmpty()) {" + " size += $tag_size$;\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeRawVarint32Size(dataSize);\n" + "}"); + } else { + printer->Print(variables_, + "size += $tag_size$ * get$capitalized_name$List().size();\n"); + } + + // cache the data size for packed fields. + if (descriptor_->options().packed()) { + printer->Print(variables_, + "$name$MemoizedSerializedSize = dataSize;\n"); + } + + printer->Outdent(); + printer->Print("}\n"); } string RepeatedEnumFieldGenerator::GetBoxedType() const { diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc index 3fdd1d57..9a4b2f79 100644 --- a/src/google/protobuf/compiler/java/java_message.cc +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -41,7 +41,7 @@ #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/wire_format_inl.h> #include <google/protobuf/descriptor.pb.h> namespace google { @@ -500,6 +500,7 @@ void MessageGenerator::GenerateBuilder(io::Printer* printer) { "public static Builder newBuilder($classname$ prototype) {\n" " return new Builder().mergeFrom(prototype);\n" "}\n" + "public Builder toBuilder() { return newBuilder(this); }\n" "\n", "classname", ClassName(descriptor_)); @@ -634,6 +635,13 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { } printer->Outdent(); + + // if message type has extensions + if (descriptor_->extension_range_count() > 0) { + printer->Print( + " this.mergeExtensionFields(other);\n"); + } + printer->Print( " this.mergeUnknownFields(other.getUnknownFields());\n" " return this;\n" @@ -692,7 +700,7 @@ void MessageGenerator::GenerateBuilderParsingMethods(io::Printer* printer) { for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = sorted_fields[i]; uint32 tag = WireFormat::MakeTag(field->number(), - WireFormat::WireTypeForFieldType(field->type())); + WireFormat::WireTypeForField(field)); printer->Print( "case $tag$: {\n", diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc index c85a1598..bbddddde 100644 --- a/src/google/protobuf/compiler/java/java_message_field.cc +++ b/src/google/protobuf/compiler/java/java_message_field.cc @@ -94,6 +94,9 @@ GenerateBuilderMembers(io::Printer* printer) const { " return result.get$capitalized_name$();\n" "}\n" "public Builder set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" " result.has$capitalized_name$ = true;\n" " result.$name$_ = value;\n" " return this;\n" @@ -216,6 +219,9 @@ GenerateBuilderMembers(io::Printer* printer) const { " return result.get$capitalized_name$(index);\n" "}\n" "public Builder set$capitalized_name$(int index, $type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" " result.$name$_.set(index, value);\n" " return this;\n" "}\n" @@ -225,6 +231,9 @@ GenerateBuilderMembers(io::Printer* printer) const { " return this;\n" "}\n" "public Builder add$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" " if (result.$name$_.isEmpty()) {\n" " result.$name$_ = new java.util.ArrayList<$type$>();\n" " }\n" diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc index fb4e650f..798e8608 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -39,6 +39,7 @@ #include <google/protobuf/stubs/common.h> #include <google/protobuf/compiler/java/java_helpers.h> #include <google/protobuf/io/printer.h> +#include <google/protobuf/wire_format_inl.h> #include <google/protobuf/stubs/strutil.h> #include <google/protobuf/stubs/substitute.h> @@ -47,6 +48,8 @@ namespace protobuf { namespace compiler { namespace java { +using internal::WireFormat; + namespace { const char* PrimitiveTypeName(JavaType type) { @@ -69,6 +72,26 @@ const char* PrimitiveTypeName(JavaType type) { return NULL; } +bool IsReferenceType(JavaType type) { + switch (type) { + case JAVATYPE_INT : return false; + case JAVATYPE_LONG : return false; + case JAVATYPE_FLOAT : return false; + case JAVATYPE_DOUBLE : return false; + case JAVATYPE_BOOLEAN: return false; + case JAVATYPE_STRING : return true; + case JAVATYPE_BYTES : return true; + case JAVATYPE_ENUM : return true; + case JAVATYPE_MESSAGE: return true; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return false; +} + const char* GetCapitalizedType(const FieldDescriptor* field) { switch (field->type()) { case FieldDescriptor::TYPE_INT32 : return "Int32" ; @@ -108,6 +131,38 @@ bool AllPrintableAscii(const string& text) { return true; } +// For encodings with fixed sizes, returns that size in bytes. Otherwise +// returns -1. +int FixedSize(FieldDescriptor::Type type) { + switch (type) { + case FieldDescriptor::TYPE_INT32 : return -1; + case FieldDescriptor::TYPE_INT64 : return -1; + case FieldDescriptor::TYPE_UINT32 : return -1; + case FieldDescriptor::TYPE_UINT64 : return -1; + case FieldDescriptor::TYPE_SINT32 : return -1; + case FieldDescriptor::TYPE_SINT64 : return -1; + case FieldDescriptor::TYPE_FIXED32 : return WireFormat::kFixed32Size; + case FieldDescriptor::TYPE_FIXED64 : return WireFormat::kFixed64Size; + case FieldDescriptor::TYPE_SFIXED32: return WireFormat::kSFixed32Size; + case FieldDescriptor::TYPE_SFIXED64: return WireFormat::kSFixed64Size; + case FieldDescriptor::TYPE_FLOAT : return WireFormat::kFloatSize; + case FieldDescriptor::TYPE_DOUBLE : return WireFormat::kDoubleSize; + + case FieldDescriptor::TYPE_BOOL : return WireFormat::kBoolSize; + case FieldDescriptor::TYPE_ENUM : return -1; + + case FieldDescriptor::TYPE_STRING : return -1; + case FieldDescriptor::TYPE_BYTES : return -1; + case FieldDescriptor::TYPE_GROUP : return -1; + case FieldDescriptor::TYPE_MESSAGE : return -1; + + // No default because we want the compiler to complain if any new + // types are added. + } + GOOGLE_LOG(FATAL) << "Can't get here."; + return -1; +} + string DefaultValue(const FieldDescriptor* field) { // Switch on cpp_type since we need to know which default_value_* method // of FieldDescriptor to call. @@ -177,8 +232,22 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, (*variables)["boxed_type"] = BoxedPrimitiveTypeName(GetJavaType(descriptor)); (*variables)["default"] = DefaultValue(descriptor); (*variables)["capitalized_type"] = GetCapitalizedType(descriptor); + (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), descriptor->type())); + if (IsReferenceType(GetJavaType(descriptor))) { + (*variables)["null_check"] = + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n"; + } else { + (*variables)["null_check"] = ""; + } + int fixed_size = FixedSize(descriptor->type()); + if (fixed_size != -1) { + (*variables)["fixed_size"] = SimpleItoa(fixed_size); + } } - } // namespace // =================================================================== @@ -210,6 +279,7 @@ GenerateBuilderMembers(io::Printer* printer) const { " return result.get$capitalized_name$();\n" "}\n" "public Builder set$capitalized_name$($type$ value) {\n" + "$null_check$" " result.has$capitalized_name$ = true;\n" " result.$name$_ = value;\n" " return this;\n" @@ -283,6 +353,12 @@ GenerateMembers(io::Printer* printer) const { "public $type$ get$capitalized_name$(int index) {\n" " return $name$_.get(index);\n" "}\n"); + + if (descriptor_->options().packed() && + descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + printer->Print(variables_, + "private int $name$MemoizedSerializedSize;\n"); + } } void RepeatedPrimitiveFieldGenerator:: @@ -302,10 +378,12 @@ GenerateBuilderMembers(io::Printer* printer) const { " return result.get$capitalized_name$(index);\n" "}\n" "public Builder set$capitalized_name$(int index, $type$ value) {\n" + "$null_check$" " result.$name$_.set(index, value);\n" " return this;\n" "}\n" "public Builder add$capitalized_name$($type$ value) {\n" + "$null_check$" " if (result.$name$_.isEmpty()) {\n" " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\n" " }\n" @@ -348,25 +426,80 @@ GenerateBuildingCode(io::Printer* printer) const { void RepeatedPrimitiveFieldGenerator:: GenerateParsingCode(io::Printer* printer) const { - printer->Print(variables_, - "add$capitalized_name$(input.read$capitalized_type$());\n"); + if (descriptor_->options().packed()) { + printer->Print(variables_, + "int length = input.readRawVarint32();\n" + "int limit = input.pushLimit(length);\n" + "while (input.getBytesUntilLimit() > 0) {\n" + " add$capitalized_name$(input.read$capitalized_type$());\n" + "}\n" + "input.popLimit(limit);\n"); + } else { + printer->Print(variables_, + "add$capitalized_name$(input.read$capitalized_type$());\n"); + } } void RepeatedPrimitiveFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { - printer->Print(variables_, - "for ($type$ element : get$capitalized_name$List()) {\n" - " output.write$capitalized_type$($number$, element);\n" - "}\n"); + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (get$capitalized_name$List().size() > 0) {\n" + " output.writeRawVarint32($tag$);\n" + " output.writeRawVarint32($name$MemoizedSerializedSize);\n" + "}\n" + "for ($type$ element : get$capitalized_name$List()) {\n" + " output.write$capitalized_type$NoTag(element);\n" + "}\n"); + } else { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " output.write$capitalized_type$($number$, element);\n" + "}\n"); + } } void RepeatedPrimitiveFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, - "for ($type$ element : get$capitalized_name$List()) {\n" - " size += com.google.protobuf.CodedOutputStream\n" - " .compute$capitalized_type$Size($number$, element);\n" - "}\n"); + "{\n" + " int dataSize = 0;\n"); + printer->Indent(); + + if (FixedSize(descriptor_->type()) == -1) { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " dataSize += com.google.protobuf.CodedOutputStream\n" + " .compute$capitalized_type$SizeNoTag(element);\n" + "}\n"); + } else { + printer->Print(variables_, + "dataSize = $fixed_size$ * get$capitalized_name$List().size();\n"); + } + + printer->Print( + "size += dataSize;\n"); + + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (!get$capitalized_name$List().isEmpty()) {\n" + " size += $tag_size$;\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeInt32SizeNoTag(dataSize);\n" + "}\n"); + } else { + printer->Print(variables_, + "size += $tag_size$ * get$capitalized_name$List().size();\n"); + } + + // cache the data size for packed fields. + if (descriptor_->options().packed()) { + printer->Print(variables_, + "$name$MemoizedSerializedSize = dataSize;\n"); + } + + printer->Outdent(); + printer->Print("}\n"); } string RepeatedPrimitiveFieldGenerator::GetBoxedType() const { diff --git a/src/google/protobuf/compiler/python/python_generator.cc b/src/google/protobuf/compiler/python/python_generator.cc index 754dcbda..ca69fd4c 100644 --- a/src/google/protobuf/compiler/python/python_generator.cc +++ b/src/google/protobuf/compiler/python/python_generator.cc @@ -111,7 +111,6 @@ void PrintTopBoilerplate( io::Printer* printer, const FileDescriptor* file, bool descriptor_proto) { // TODO(robinson): Allow parameterization of Python version? printer->Print( - "#!/usr/bin/python2.4\n" "# Generated by the protocol buffer compiler. DO NOT EDIT!\n" "\n" "from google.protobuf import descriptor\n" diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc index 21c709fb..eb5b5937 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -122,6 +122,35 @@ namespace { const string kEmptyString; +string ToCamelCase(const string& input) { + bool capitalize_next = false; + string result; + result.reserve(input.size()); + + for (int i = 0; i < input.size(); i++) { + if (input[i] == '_') { + capitalize_next = true; + } else if (capitalize_next) { + // Note: I distrust ctype.h due to locales. + if ('a' <= input[i] && input[i] <= 'z') { + result.push_back(input[i] - 'a' + 'A'); + } else { + result.push_back(input[i]); + } + capitalize_next = false; + } else { + result.push_back(input[i]); + } + } + + // Lower-case the first letter. + if (!result.empty() && 'A' <= result[0] && result[0] <= 'Z') { + result[0] = result[0] - 'A' + 'a'; + } + + return result; +} + // A DescriptorPool contains a bunch of hash_maps to implement the // various Find*By*() methods. Since hashtable lookups are O(1), it's // most efficient to construct a fixed set of large hash_maps used by @@ -253,6 +282,9 @@ typedef hash_map<PointerStringPair, Symbol, typedef hash_map<const char*, const FileDescriptor*, hash<const char*>, CStringEqual> FilesByNameMap; +typedef hash_map<PointerStringPair, const FieldDescriptor*, + PointerStringPairHash, PointerStringPairEqual> + FieldsByNameMap; typedef hash_map<DescriptorIntPair, const FieldDescriptor*, PointerIntegerPairHash<DescriptorIntPair> > FieldsByNumberMap; @@ -296,21 +328,29 @@ class DescriptorPool::Tables { // Finding items. // Find symbols. These return a null Symbol (symbol.IsNull() is true) - // if not found. FindSymbolOfType() additionally returns null if the - // symbol is not of the given type. + // if not found. inline Symbol FindSymbol(const string& key) const; - inline Symbol FindSymbolOfType(const string& key, - const Symbol::Type type) const; inline Symbol FindNestedSymbol(const void* parent, const string& name) const; inline Symbol FindNestedSymbolOfType(const void* parent, const string& name, const Symbol::Type type) const; + // This implements the body of DescriptorPool::Find*ByName(). It should + // really be a private method of DescriptorPool, but that would require + // declaring Symbol in descriptor.h, which would drag all kinds of other + // stuff into the header. Yay C++. + Symbol FindByNameHelper( + const DescriptorPool* pool, const string& name) const; + // These return NULL if not found. inline const FileDescriptor* FindFile(const string& key) const; inline const FieldDescriptor* FindFieldByNumber( const Descriptor* parent, int number) const; + inline const FieldDescriptor* FindFieldByLowercaseName( + const void* parent, const string& lowercase_name) const; + inline const FieldDescriptor* FindFieldByCamelcaseName( + const void* parent, const string& camelcase_name) const; inline const EnumValueDescriptor* FindEnumValueByNumber( const EnumDescriptor* parent, int number) const; @@ -330,6 +370,10 @@ class DescriptorPool::Tables { bool AddFieldByNumber(const FieldDescriptor* field); bool AddEnumValueByNumber(const EnumValueDescriptor* value); + // Adds the field to the lowercase_name and camelcase_name maps. Never + // fails because we allow duplicates; the first field by the name wins. + void AddFieldByStylizedNames(const FieldDescriptor* field); + // Like AddSymbol(), but only adds to symbols_by_parent_, not // symbols_by_name_. Used for enum values, which need to be registered // under multiple parents (their type and its parent). @@ -364,6 +408,8 @@ class DescriptorPool::Tables { SymbolsByNameMap symbols_by_name_; SymbolsByParentMap symbols_by_parent_; FilesByNameMap files_by_name_; + FieldsByNameMap fields_by_lowercase_name_; + FieldsByNameMap fields_by_camelcase_name_; FieldsByNumberMap fields_by_number_; // Includes extensions. EnumValuesByNumberMap enum_values_by_number_; @@ -373,6 +419,8 @@ class DescriptorPool::Tables { vector<const char* > symbols_after_checkpoint_; vector<PointerStringPair> symbols_by_parent_after_checkpoint_; vector<const char* > files_after_checkpoint_; + vector<PointerStringPair> field_lowercase_names_after_checkpoint_; + vector<PointerStringPair> field_camelcase_names_after_checkpoint_; vector<DescriptorIntPair> field_numbers_after_checkpoint_; vector<EnumIntPair > enum_numbers_after_checkpoint_; @@ -404,6 +452,8 @@ void DescriptorPool::Tables::Checkpoint() { symbols_after_checkpoint_.clear(); symbols_by_parent_after_checkpoint_.clear(); files_after_checkpoint_.clear(); + field_lowercase_names_after_checkpoint_.clear(); + field_camelcase_names_after_checkpoint_.clear(); field_numbers_after_checkpoint_.clear(); enum_numbers_after_checkpoint_.clear(); } @@ -418,6 +468,12 @@ void DescriptorPool::Tables::Rollback() { for (int i = 0; i < files_after_checkpoint_.size(); i++) { files_by_name_.erase(files_after_checkpoint_[i]); } + for (int i = 0; i < field_lowercase_names_after_checkpoint_.size(); i++) { + fields_by_lowercase_name_.erase(field_lowercase_names_after_checkpoint_[i]); + } + for (int i = 0; i < field_camelcase_names_after_checkpoint_.size(); i++) { + fields_by_camelcase_name_.erase(field_camelcase_names_after_checkpoint_[i]); + } for (int i = 0; i < field_numbers_after_checkpoint_.size(); i++) { fields_by_number_.erase(field_numbers_after_checkpoint_[i]); } @@ -428,6 +484,8 @@ void DescriptorPool::Tables::Rollback() { symbols_after_checkpoint_.clear(); symbols_by_parent_after_checkpoint_.clear(); files_after_checkpoint_.clear(); + field_lowercase_names_after_checkpoint_.clear(); + field_camelcase_names_after_checkpoint_.clear(); field_numbers_after_checkpoint_.clear(); enum_numbers_after_checkpoint_.clear(); @@ -455,13 +513,6 @@ inline Symbol DescriptorPool::Tables::FindSymbol(const string& key) const { } } -inline Symbol DescriptorPool::Tables::FindSymbolOfType( - const string& key, const Symbol::Type type) const { - Symbol result = FindSymbol(key); - if (result.type != type) return kNullSymbol; - return result; -} - inline Symbol DescriptorPool::Tables::FindNestedSymbol( const void* parent, const string& name) const { const Symbol* result = @@ -480,6 +531,27 @@ inline Symbol DescriptorPool::Tables::FindNestedSymbolOfType( return result; } +Symbol DescriptorPool::Tables::FindByNameHelper( + const DescriptorPool* pool, const string& name) const { + MutexLockMaybe lock(pool->mutex_); + Symbol result = FindSymbol(name); + + if (result.IsNull() && pool->underlay_ != NULL) { + // Symbol not found; check the underlay. + result = + pool->underlay_->tables_->FindByNameHelper(pool->underlay_, name); + } + + if (result.IsNull()) { + // Symbol still not found, so check fallback database. + if (pool->TryFindSymbolInFallbackDatabase(name)) { + result = FindSymbol(name); + } + } + + return result; +} + inline const FileDescriptor* DescriptorPool::Tables::FindFile( const string& key) const { return FindPtrOrNull(files_by_name_, key.c_str()); @@ -490,6 +562,18 @@ inline const FieldDescriptor* DescriptorPool::Tables::FindFieldByNumber( return FindPtrOrNull(fields_by_number_, make_pair(parent, number)); } +inline const FieldDescriptor* DescriptorPool::Tables::FindFieldByLowercaseName( + const void* parent, const string& lowercase_name) const { + return FindPtrOrNull(fields_by_lowercase_name_, + PointerStringPair(parent, lowercase_name.c_str())); +} + +inline const FieldDescriptor* DescriptorPool::Tables::FindFieldByCamelcaseName( + const void* parent, const string& camelcase_name) const { + return FindPtrOrNull(fields_by_camelcase_name_, + PointerStringPair(parent, camelcase_name.c_str())); +} + inline const EnumValueDescriptor* DescriptorPool::Tables::FindEnumValueByNumber( const EnumDescriptor* parent, int number) const { return FindPtrOrNull(enum_values_by_number_, make_pair(parent, number)); @@ -537,6 +621,30 @@ bool DescriptorPool::Tables::AddFile(const FileDescriptor* file) { } } +void DescriptorPool::Tables::AddFieldByStylizedNames( + const FieldDescriptor* field) { + const void* parent; + if (field->is_extension()) { + if (field->extension_scope() == NULL) { + parent = field->file(); + } else { + parent = field->extension_scope(); + } + } else { + parent = field->containing_type(); + } + + PointerStringPair lowercase_key(parent, field->lowercase_name().c_str()); + if (InsertIfNotPresent(&fields_by_lowercase_name_, lowercase_key, field)) { + field_lowercase_names_after_checkpoint_.push_back(lowercase_key); + } + + PointerStringPair camelcase_key(parent, field->camelcase_name().c_str()); + if (InsertIfNotPresent(&fields_by_camelcase_name_, camelcase_key, field)) { + field_camelcase_names_after_checkpoint_.push_back(camelcase_key); + } +} + bool DescriptorPool::Tables::AddFieldByNumber(const FieldDescriptor* field) { DescriptorIntPair key(field->containing_type(), field->number()); if (InsertIfNotPresent(&fields_by_number_, key, field)) { @@ -689,122 +797,55 @@ const FileDescriptor* DescriptorPool::FindFileContainingSymbol( const Descriptor* DescriptorPool::FindMessageTypeByName( const string& name) const { - MutexLockMaybe lock(mutex_); - Symbol result = tables_->FindSymbolOfType(name, Symbol::MESSAGE); - if (!result.IsNull()) return result.descriptor; - if (underlay_ != NULL) { - const Descriptor* result = underlay_->FindMessageTypeByName(name); - if (result != NULL) return result; - } - if (TryFindSymbolInFallbackDatabase(name)) { - Symbol result = tables_->FindSymbolOfType(name, Symbol::MESSAGE); - if (!result.IsNull()) return result.descriptor; - } - return NULL; + Symbol result = tables_->FindByNameHelper(this, name); + return (result.type == Symbol::MESSAGE) ? result.descriptor : NULL; } const FieldDescriptor* DescriptorPool::FindFieldByName( const string& name) const { - MutexLockMaybe lock(mutex_); - Symbol result = tables_->FindSymbolOfType(name, Symbol::FIELD); - if (!result.IsNull() && !result.field_descriptor->is_extension()) { + Symbol result = tables_->FindByNameHelper(this, name); + if (result.type == Symbol::FIELD && + !result.field_descriptor->is_extension()) { return result.field_descriptor; + } else { + return NULL; } - if (underlay_ != NULL) { - const FieldDescriptor* result = underlay_->FindFieldByName(name); - if (result != NULL) return result; - } - if (TryFindSymbolInFallbackDatabase(name)) { - Symbol result = tables_->FindSymbolOfType(name, Symbol::FIELD); - if (!result.IsNull() && !result.field_descriptor->is_extension()) { - return result.field_descriptor; - } - } - return NULL; } const FieldDescriptor* DescriptorPool::FindExtensionByName( const string& name) const { - MutexLockMaybe lock(mutex_); - Symbol result = tables_->FindSymbolOfType(name, Symbol::FIELD); - if (!result.IsNull() && result.field_descriptor->is_extension()) { + Symbol result = tables_->FindByNameHelper(this, name); + if (result.type == Symbol::FIELD && + result.field_descriptor->is_extension()) { return result.field_descriptor; + } else { + return NULL; } - if (underlay_ != NULL) { - const FieldDescriptor* result = underlay_->FindExtensionByName(name); - if (result != NULL) return result; - } - if (TryFindSymbolInFallbackDatabase(name)) { - Symbol result = tables_->FindSymbolOfType(name, Symbol::FIELD); - if (!result.IsNull() && result.field_descriptor->is_extension()) { - return result.field_descriptor; - } - } - return NULL; } const EnumDescriptor* DescriptorPool::FindEnumTypeByName( const string& name) const { - MutexLockMaybe lock(mutex_); - Symbol result = tables_->FindSymbolOfType(name, Symbol::ENUM); - if (!result.IsNull()) return result.enum_descriptor; - if (underlay_ != NULL) { - const EnumDescriptor* result = underlay_->FindEnumTypeByName(name); - if (result != NULL) return result; - } - if (TryFindSymbolInFallbackDatabase(name)) { - Symbol result = tables_->FindSymbolOfType(name, Symbol::ENUM); - if (!result.IsNull()) return result.enum_descriptor; - } - return NULL; + Symbol result = tables_->FindByNameHelper(this, name); + return (result.type == Symbol::ENUM) ? result.enum_descriptor : NULL; } const EnumValueDescriptor* DescriptorPool::FindEnumValueByName( const string& name) const { - MutexLockMaybe lock(mutex_); - Symbol result = tables_->FindSymbolOfType(name, Symbol::ENUM_VALUE); - if (!result.IsNull()) return result.enum_value_descriptor; - if (underlay_ != NULL) { - const EnumValueDescriptor* result = underlay_->FindEnumValueByName(name); - if (result != NULL) return result; - } - if (TryFindSymbolInFallbackDatabase(name)) { - Symbol result = tables_->FindSymbolOfType(name, Symbol::ENUM_VALUE); - if (!result.IsNull()) return result.enum_value_descriptor; - } - return NULL; + Symbol result = tables_->FindByNameHelper(this, name); + return (result.type == Symbol::ENUM_VALUE) ? + result.enum_value_descriptor : NULL; } const ServiceDescriptor* DescriptorPool::FindServiceByName( const string& name) const { - MutexLockMaybe lock(mutex_); - Symbol result = tables_->FindSymbolOfType(name, Symbol::SERVICE); - if (!result.IsNull()) return result.service_descriptor; - if (underlay_ != NULL) { - const ServiceDescriptor* result = underlay_->FindServiceByName(name); - if (result != NULL) return result; - } - if (TryFindSymbolInFallbackDatabase(name)) { - Symbol result = tables_->FindSymbolOfType(name, Symbol::SERVICE); - if (!result.IsNull()) return result.service_descriptor; - } - return NULL; + Symbol result = tables_->FindByNameHelper(this, name); + return (result.type == Symbol::SERVICE) ? result.service_descriptor : NULL; } const MethodDescriptor* DescriptorPool::FindMethodByName( const string& name) const { - MutexLockMaybe lock(mutex_); - Symbol result = tables_->FindSymbolOfType(name, Symbol::METHOD); - if (!result.IsNull()) return result.method_descriptor; - if (underlay_ != NULL) { - const MethodDescriptor* result = underlay_->FindMethodByName(name); - if (result != NULL) return result; - } - if (TryFindSymbolInFallbackDatabase(name)) { - Symbol result = tables_->FindSymbolOfType(name, Symbol::METHOD); - if (!result.IsNull()) return result.method_descriptor; - } - return NULL; + Symbol result = tables_->FindByNameHelper(this, name); + return (result.type == Symbol::METHOD) ? result.method_descriptor : NULL; } const FieldDescriptor* DescriptorPool::FindExtensionByNumber( @@ -844,6 +885,30 @@ Descriptor::FindFieldByNumber(int key) const { } const FieldDescriptor* +Descriptor::FindFieldByLowercaseName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + const FieldDescriptor* result = + file()->pool()->tables_->FindFieldByLowercaseName(this, key); + if (result == NULL || result->is_extension()) { + return NULL; + } else { + return result; + } +} + +const FieldDescriptor* +Descriptor::FindFieldByCamelcaseName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + const FieldDescriptor* result = + file()->pool()->tables_->FindFieldByCamelcaseName(this, key); + if (result == NULL || result->is_extension()) { + return NULL; + } else { + return result; + } +} + +const FieldDescriptor* Descriptor::FindFieldByName(const string& key) const { MutexLockMaybe lock(file()->pool()->mutex_); Symbol result = @@ -867,6 +932,30 @@ Descriptor::FindExtensionByName(const string& key) const { } } +const FieldDescriptor* +Descriptor::FindExtensionByLowercaseName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + const FieldDescriptor* result = + file()->pool()->tables_->FindFieldByLowercaseName(this, key); + if (result == NULL || !result->is_extension()) { + return NULL; + } else { + return result; + } +} + +const FieldDescriptor* +Descriptor::FindExtensionByCamelcaseName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + const FieldDescriptor* result = + file()->pool()->tables_->FindFieldByCamelcaseName(this, key); + if (result == NULL || !result->is_extension()) { + return NULL; + } else { + return result; + } +} + const Descriptor* Descriptor::FindNestedTypeByName(const string& key) const { MutexLockMaybe lock(file()->pool()->mutex_); @@ -995,6 +1084,30 @@ FileDescriptor::FindExtensionByName(const string& key) const { } } +const FieldDescriptor* +FileDescriptor::FindExtensionByLowercaseName(const string& key) const { + MutexLockMaybe lock(pool()->mutex_); + const FieldDescriptor* result = + pool()->tables_->FindFieldByLowercaseName(this, key); + if (result == NULL || !result->is_extension()) { + return NULL; + } else { + return result; + } +} + +const FieldDescriptor* +FileDescriptor::FindExtensionByCamelcaseName(const string& key) const { + MutexLockMaybe lock(pool()->mutex_); + const FieldDescriptor* result = + pool()->tables_->FindFieldByCamelcaseName(this, key); + if (result == NULL || !result->is_extension()) { + return NULL; + } else { + return result; + } +} + bool Descriptor::IsExtensionNumber(int number) const { // Linear search should be fine because we don't expect a message to have // more than a couple extension ranges. @@ -2504,6 +2617,22 @@ void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, result->number_ = proto.number(); result->is_extension_ = is_extension; + // If .proto files follow the style guide then the name should already be + // lower-cased. If that's the case we can just reuse the string we already + // allocated rather than allocate a new one. + string lowercase_name(proto.name()); + LowerString(&lowercase_name); + if (lowercase_name == proto.name()) { + result->lowercase_name_ = result->name_; + } else { + result->lowercase_name_ = tables_->AllocateString(lowercase_name); + } + + // Don't bother with the above optimization for camel-case names since + // .proto files that follow the guide shouldn't be using names in this + // format, so the optimization wouldn't help much. + result->camelcase_name_ = tables_->AllocateString(ToCamelCase(proto.name())); + // Some compilers do not allow static_cast directly between two enum types, // so we must cast to int first. result->type_ = static_cast<FieldDescriptor::Type>( @@ -3042,6 +3171,9 @@ void DescriptorBuilder::CrossLinkField( conflicting_field->name())); } } + + // Add the field to the lowercase-name and camelcase-name tables. + tables_->AddFieldByStylizedNames(field); } void DescriptorBuilder::CrossLinkEnum( @@ -3136,6 +3268,20 @@ void DescriptorBuilder::ValidateFieldOptions(FieldDescriptor* field, ValidateMapKey(field, proto); } + // Only repeated primitive fields may be packed. + if (field->options().packed()) { + if (!field->is_repeated() || + field->type() == FieldDescriptor::TYPE_STRING || + field->type() == FieldDescriptor::TYPE_GROUP || + field->type() == FieldDescriptor::TYPE_MESSAGE || + field->type() == FieldDescriptor::TYPE_BYTES) { + AddError( + field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + "[packed = true] can only be specified for repeated primitive fields."); + } + } + // Note: Default instance may not yet be initialized here, so we have to // avoid reading from it. if (field->containing_type_ != NULL && diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h index 6d496e99..918aafbc 100644 --- a/src/google/protobuf/descriptor.h +++ b/src/google/protobuf/descriptor.h @@ -156,6 +156,19 @@ class LIBPROTOBUF_EXPORT Descriptor { // Looks up a field by name. Returns NULL if no such field exists. const FieldDescriptor* FindFieldByName(const string& name) const; + // Looks up a field by lowercased name (as returned by lowercase_name()). + // This lookup may be ambiguous if multiple field names differ only by case, + // in which case the field returned is chosen arbitrarily from the matches. + const FieldDescriptor* FindFieldByLowercaseName( + const string& lowercase_name) const; + + // Looks up a field by camel-case name (as returned by camelcase_name()). + // This lookup may be ambiguous if multiple field names differ in a way that + // leads them to have identical camel-case names, in which case the field + // returned is chosen arbitrarily from the matches. + const FieldDescriptor* FindFieldByCamelcaseName( + const string& camelcase_name) const; + // Nested type stuff ----------------------------------------------- // The number of nested types in this message type. @@ -213,6 +226,14 @@ class LIBPROTOBUF_EXPORT Descriptor { // defined within this message type's scope. const FieldDescriptor* FindExtensionByName(const string& name) const; + // Similar to FindFieldByLowercaseName(), but finds extensions defined within + // this message type's scope. + const FieldDescriptor* FindExtensionByLowercaseName(const string& name) const; + + // Similar to FindFieldByCamelcaseName(), but finds extensions defined within + // this message type's scope. + const FieldDescriptor* FindExtensionByCamelcaseName(const string& name) const; + private: typedef MessageOptions OptionsType; @@ -336,6 +357,25 @@ class LIBPROTOBUF_EXPORT FieldDescriptor { bool is_extension() const; // Is this an extension field? int number() const; // Declared tag number. + // Same as name() except converted to lower-case. This (and especially the + // FindFieldByLowercaseName() method) can be useful when parsing formats + // which prefer to use lowercase naming style. (Although, technically + // field names should be lowercased anyway according to the protobuf style + // guide, so this only makes a difference when dealing with old .proto files + // which do not follow the guide.) + const string& lowercase_name() const; + + // Same as name() except converted to camel-case. In this conversion, any + // time an underscore appears in the name, it is removed and the next + // letter is capitalized. Furthermore, the first letter of the name is + // lower-cased. Examples: + // FooBar -> fooBar + // foo_bar -> fooBar + // fooBar -> fooBar + // This (and especially the FindFieldByCamelcaseName() method) can be useful + // when parsing formats which prefer to use camel-case naming style. + const string& camelcase_name() const; + Type type() const; // Declared type of this field. CppType cpp_type() const; // C++ type of this field. Label label() const; // optional/required/repeated @@ -431,6 +471,8 @@ class LIBPROTOBUF_EXPORT FieldDescriptor { const string* name_; const string* full_name_; + const string* lowercase_name_; + const string* camelcase_name_; const FileDescriptor* file_; int number_; Type type_; @@ -773,6 +815,12 @@ class LIBPROTOBUF_EXPORT FileDescriptor { const ServiceDescriptor* FindServiceByName(const string& name) const; // Find a top-level extension definition by name. Returns NULL if not found. const FieldDescriptor* FindExtensionByName(const string& name) const; + // Similar to FindExtensionByName(), but searches by lowercased-name. See + // Descriptor::FindFieldByLowercaseName(). + const FieldDescriptor* FindExtensionByLowercaseName(const string& name) const; + // Similar to FindExtensionByName(), but searches by camelcased-name. See + // Descriptor::FindFieldByCamelcaseName(). + const FieldDescriptor* FindExtensionByCamelcaseName(const string& name) const; // See Descriptor::CopyTo(). void CopyTo(FileDescriptorProto* proto) const; @@ -1084,6 +1132,8 @@ PROTOBUF_DEFINE_OPTIONS_ACCESSOR(Descriptor, MessageOptions); PROTOBUF_DEFINE_STRING_ACCESSOR(FieldDescriptor, name) PROTOBUF_DEFINE_STRING_ACCESSOR(FieldDescriptor, full_name) +PROTOBUF_DEFINE_STRING_ACCESSOR(FieldDescriptor, lowercase_name) +PROTOBUF_DEFINE_STRING_ACCESSOR(FieldDescriptor, camelcase_name) PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, file, const FileDescriptor*) PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, number, int) PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, is_extension, bool) diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc index ceb99bf1..0df3f3bd 100644 --- a/src/google/protobuf/descriptor.pb.cc +++ b/src/google/protobuf/descriptor.pb.cc @@ -301,8 +301,9 @@ void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto_AssignGlobalDescr MessageOptions_descriptor_, MessageOptions::default_instance_); FieldOptions_descriptor_ = file->message_type(10); FieldOptions::default_instance_ = new FieldOptions(); - static const int FieldOptions_offsets_[3] = { + static const int FieldOptions_offsets_[4] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, ctype_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, packed_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, experimental_map_key_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, uninterpreted_option_), }; @@ -519,30 +520,30 @@ void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto() { "\n\016MessageOptions\022&\n\027message_set_wire_for" "mat\030\001 \001(\010:\005false\022C\n\024uninterpreted_option" "\030\347\007 \003(\0132$.google.protobuf.UninterpretedO" - "ption*\t\010\350\007\020\200\200\200\200\002\"\325\001\n\014FieldOptions\0222\n\005cty" + "ption*\t\010\350\007\020\200\200\200\200\002\"\345\001\n\014FieldOptions\0222\n\005cty" "pe\030\001 \001(\0162#.google.protobuf.FieldOptions." - "CType\022\034\n\024experimental_map_key\030\t \001(\t\022C\n\024u" - "ninterpreted_option\030\347\007 \003(\0132$.google.prot" - "obuf.UninterpretedOption\"#\n\005CType\022\010\n\004COR" - "D\020\001\022\020\n\014STRING_PIECE\020\002*\t\010\350\007\020\200\200\200\200\002\"]\n\013Enum" - "Options\022C\n\024uninterpreted_option\030\347\007 \003(\0132$" - ".google.protobuf.UninterpretedOption*\t\010\350" - "\007\020\200\200\200\200\002\"b\n\020EnumValueOptions\022C\n\024uninterpr" - "eted_option\030\347\007 \003(\0132$.google.protobuf.Uni" - "nterpretedOption*\t\010\350\007\020\200\200\200\200\002\"`\n\016ServiceOp" - "tions\022C\n\024uninterpreted_option\030\347\007 \003(\0132$.g" - "oogle.protobuf.UninterpretedOption*\t\010\350\007\020" - "\200\200\200\200\002\"_\n\rMethodOptions\022C\n\024uninterpreted_" - "option\030\347\007 \003(\0132$.google.protobuf.Uninterp" - "retedOption*\t\010\350\007\020\200\200\200\200\002\"\205\002\n\023Uninterpreted" - "Option\022;\n\004name\030\002 \003(\0132-.google.protobuf.U" - "ninterpretedOption.NamePart\022\030\n\020identifie" - "r_value\030\003 \001(\t\022\032\n\022positive_int_value\030\004 \001(" - "\004\022\032\n\022negative_int_value\030\005 \001(\003\022\024\n\014double_" - "value\030\006 \001(\001\022\024\n\014string_value\030\007 \001(\014\0323\n\010Nam" - "ePart\022\021\n\tname_part\030\001 \002(\t\022\024\n\014is_extension" - "\030\002 \002(\010B)\n\023com.google.protobufB\020Descripto" - "rProtosH\001", 3449, + "CType\022\016\n\006packed\030\002 \001(\010\022\034\n\024experimental_ma" + "p_key\030\t \001(\t\022C\n\024uninterpreted_option\030\347\007 \003" + "(\0132$.google.protobuf.UninterpretedOption" + "\"#\n\005CType\022\010\n\004CORD\020\001\022\020\n\014STRING_PIECE\020\002*\t\010" + "\350\007\020\200\200\200\200\002\"]\n\013EnumOptions\022C\n\024uninterpreted" + "_option\030\347\007 \003(\0132$.google.protobuf.Uninter" + "pretedOption*\t\010\350\007\020\200\200\200\200\002\"b\n\020EnumValueOpti" + "ons\022C\n\024uninterpreted_option\030\347\007 \003(\0132$.goo" + "gle.protobuf.UninterpretedOption*\t\010\350\007\020\200\200" + "\200\200\002\"`\n\016ServiceOptions\022C\n\024uninterpreted_o" + "ption\030\347\007 \003(\0132$.google.protobuf.Uninterpr" + "etedOption*\t\010\350\007\020\200\200\200\200\002\"_\n\rMethodOptions\022C" + "\n\024uninterpreted_option\030\347\007 \003(\0132$.google.p" + "rotobuf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002\"\205" + "\002\n\023UninterpretedOption\022;\n\004name\030\002 \003(\0132-.g" + "oogle.protobuf.UninterpretedOption.NameP" + "art\022\030\n\020identifier_value\030\003 \001(\t\022\032\n\022positiv" + "e_int_value\030\004 \001(\004\022\032\n\022negative_int_value\030" + "\005 \001(\003\022\024\n\014double_value\030\006 \001(\001\022\024\n\014string_va" + "lue\030\007 \001(\014\0323\n\010NamePart\022\021\n\tname_part\030\001 \002(\t" + "\022\024\n\014is_extension\030\002 \002(\010B)\n\023com.google.pro" + "tobufB\020DescriptorProtosH\001", 3465, &protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto_AssignGlobalDescriptors); } @@ -639,7 +640,7 @@ bool FileDescriptorSet::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { #define DO_(EXPRESSION) if (!(EXPRESSION)) return false // repeated .google.protobuf.FileDescriptorProto file = 1; - for (int i = 0; i < file_.size(); i++) { + for (int i = 0; i < this->file_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(1, this->file(i), output)); } @@ -655,8 +656,8 @@ int FileDescriptorSet::ByteSize() const { int total_size = 0; // repeated .google.protobuf.FileDescriptorProto file = 1; - total_size += 1 * file_size(); - for (int i = 0; i < file_size(); i++) { + total_size += 1 * this->file_size(); + for (int i = 0; i < this->file_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->file(i)); @@ -954,27 +955,27 @@ bool FileDescriptorProto::SerializeWithCachedSizes( } // repeated string dependency = 3; - for (int i = 0; i < dependency_.size(); i++) { + for (int i = 0; i < this->dependency_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteString(3, this->dependency(i), output)); } // repeated .google.protobuf.DescriptorProto message_type = 4; - for (int i = 0; i < message_type_.size(); i++) { + for (int i = 0; i < this->message_type_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(4, this->message_type(i), output)); } // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; - for (int i = 0; i < enum_type_.size(); i++) { + for (int i = 0; i < this->enum_type_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(5, this->enum_type(i), output)); } // repeated .google.protobuf.ServiceDescriptorProto service = 6; - for (int i = 0; i < service_.size(); i++) { + for (int i = 0; i < this->service_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(6, this->service(i), output)); } // repeated .google.protobuf.FieldDescriptorProto extension = 7; - for (int i = 0; i < extension_.size(); i++) { + for (int i = 0; i < this->extension_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(7, this->extension(i), output)); } @@ -1016,39 +1017,39 @@ int FileDescriptorProto::ByteSize() const { } // repeated string dependency = 3; - total_size += 1 * dependency_size(); - for (int i = 0; i < dependency_size(); i++) { + total_size += 1 * this->dependency_size(); + for (int i = 0; i < this->dependency_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::StringSize( this->dependency(i)); } // repeated .google.protobuf.DescriptorProto message_type = 4; - total_size += 1 * message_type_size(); - for (int i = 0; i < message_type_size(); i++) { + total_size += 1 * this->message_type_size(); + for (int i = 0; i < this->message_type_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->message_type(i)); } // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; - total_size += 1 * enum_type_size(); - for (int i = 0; i < enum_type_size(); i++) { + total_size += 1 * this->enum_type_size(); + for (int i = 0; i < this->enum_type_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->enum_type(i)); } // repeated .google.protobuf.ServiceDescriptorProto service = 6; - total_size += 1 * service_size(); - for (int i = 0; i < service_size(); i++) { + total_size += 1 * this->service_size(); + for (int i = 0; i < this->service_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->service(i)); } // repeated .google.protobuf.FieldDescriptorProto extension = 7; - total_size += 1 * extension_size(); - for (int i = 0; i < extension_size(); i++) { + total_size += 1 * this->extension_size(); + for (int i = 0; i < this->extension_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->extension(i)); @@ -1564,27 +1565,27 @@ bool DescriptorProto::SerializeWithCachedSizes( } // repeated .google.protobuf.FieldDescriptorProto field = 2; - for (int i = 0; i < field_.size(); i++) { + for (int i = 0; i < this->field_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(2, this->field(i), output)); } // repeated .google.protobuf.DescriptorProto nested_type = 3; - for (int i = 0; i < nested_type_.size(); i++) { + for (int i = 0; i < this->nested_type_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(3, this->nested_type(i), output)); } // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; - for (int i = 0; i < enum_type_.size(); i++) { + for (int i = 0; i < this->enum_type_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(4, this->enum_type(i), output)); } // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; - for (int i = 0; i < extension_range_.size(); i++) { + for (int i = 0; i < this->extension_range_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(5, this->extension_range(i), output)); } // repeated .google.protobuf.FieldDescriptorProto extension = 6; - for (int i = 0; i < extension_.size(); i++) { + for (int i = 0; i < this->extension_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(6, this->extension(i), output)); } @@ -1620,40 +1621,40 @@ int DescriptorProto::ByteSize() const { } // repeated .google.protobuf.FieldDescriptorProto field = 2; - total_size += 1 * field_size(); - for (int i = 0; i < field_size(); i++) { + total_size += 1 * this->field_size(); + for (int i = 0; i < this->field_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->field(i)); } // repeated .google.protobuf.FieldDescriptorProto extension = 6; - total_size += 1 * extension_size(); - for (int i = 0; i < extension_size(); i++) { + total_size += 1 * this->extension_size(); + for (int i = 0; i < this->extension_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->extension(i)); } // repeated .google.protobuf.DescriptorProto nested_type = 3; - total_size += 1 * nested_type_size(); - for (int i = 0; i < nested_type_size(); i++) { + total_size += 1 * this->nested_type_size(); + for (int i = 0; i < this->nested_type_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->nested_type(i)); } // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; - total_size += 1 * enum_type_size(); - for (int i = 0; i < enum_type_size(); i++) { + total_size += 1 * this->enum_type_size(); + for (int i = 0; i < this->enum_type_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->enum_type(i)); } // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; - total_size += 1 * extension_range_size(); - for (int i = 0; i < extension_range_size(); i++) { + total_size += 1 * this->extension_range_size(); + for (int i = 0; i < this->extension_range_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->extension_range(i)); @@ -2407,7 +2408,7 @@ bool EnumDescriptorProto::SerializeWithCachedSizes( } // repeated .google.protobuf.EnumValueDescriptorProto value = 2; - for (int i = 0; i < value_.size(); i++) { + for (int i = 0; i < this->value_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(2, this->value(i), output)); } @@ -2443,8 +2444,8 @@ int EnumDescriptorProto::ByteSize() const { } // repeated .google.protobuf.EnumValueDescriptorProto value = 2; - total_size += 1 * value_size(); - for (int i = 0; i < value_size(); i++) { + total_size += 1 * this->value_size(); + for (int i = 0; i < this->value_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->value(i)); @@ -2919,7 +2920,7 @@ bool ServiceDescriptorProto::SerializeWithCachedSizes( } // repeated .google.protobuf.MethodDescriptorProto method = 2; - for (int i = 0; i < method_.size(); i++) { + for (int i = 0; i < this->method_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(2, this->method(i), output)); } @@ -2955,8 +2956,8 @@ int ServiceDescriptorProto::ByteSize() const { } // repeated .google.protobuf.MethodDescriptorProto method = 2; - total_size += 1 * method_size(); - for (int i = 0; i < method_size(); i++) { + total_size += 1 * this->method_size(); + for (int i = 0; i < this->method_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->method(i)); @@ -3561,7 +3562,7 @@ bool FileOptions::SerializeWithCachedSizes( } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - for (int i = 0; i < uninterpreted_option_.size(); i++) { + for (int i = 0; i < this->uninterpreted_option_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(999, this->uninterpreted_option(i), output)); } @@ -3606,8 +3607,8 @@ int FileOptions::ByteSize() const { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - total_size += 2 * uninterpreted_option_size(); - for (int i = 0; i < uninterpreted_option_size(); i++) { + total_size += 2 * this->uninterpreted_option_size(); + for (int i = 0; i < this->uninterpreted_option_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->uninterpreted_option(i)); @@ -3821,7 +3822,7 @@ bool MessageOptions::SerializeWithCachedSizes( } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - for (int i = 0; i < uninterpreted_option_.size(); i++) { + for (int i = 0; i < this->uninterpreted_option_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(999, this->uninterpreted_option(i), output)); } @@ -3848,8 +3849,8 @@ int MessageOptions::ByteSize() const { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - total_size += 2 * uninterpreted_option_size(); - for (int i = 0; i < uninterpreted_option_size(); i++) { + total_size += 2 * this->uninterpreted_option_size(); + for (int i = 0; i < this->uninterpreted_option_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->uninterpreted_option(i)); @@ -3954,6 +3955,7 @@ const FieldOptions_CType FieldOptions::CType_MIN; const FieldOptions_CType FieldOptions::CType_MAX; #endif // _MSC_VER + const ::std::string FieldOptions::_default_experimental_map_key_; FieldOptions::FieldOptions() @@ -3963,6 +3965,7 @@ FieldOptions::FieldOptions() ::google::protobuf::MessageFactory::generated_factory()), _cached_size_(0), ctype_(1), + packed_(false), experimental_map_key_(const_cast< ::std::string*>(&_default_experimental_map_key_)) { ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -3976,6 +3979,7 @@ FieldOptions::FieldOptions(const FieldOptions& from) ::google::protobuf::MessageFactory::generated_factory()), _cached_size_(0), ctype_(1), + packed_(false), experimental_map_key_(const_cast< ::std::string*>(&_default_experimental_map_key_)) { ::memset(_has_bits_, 0, sizeof(_has_bits_)); MergeFrom(from); @@ -4009,7 +4013,8 @@ void FieldOptions::Clear() { _extensions_.Clear(); if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { ctype_ = 1; - if (_has_bit(1)) { + packed_ = false; + if (_has_bit(2)) { if (experimental_map_key_ != &_default_experimental_map_key_) { experimental_map_key_->clear(); } @@ -4039,6 +4044,20 @@ bool FieldOptions::MergePartialFromCodedStream( } else { mutable_unknown_fields()->AddField(1)->add_varint(value); } + if (input->ExpectTag(16)) goto parse_packed; + break; + } + + // optional bool packed = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_packed: + DO_(::google::protobuf::internal::WireFormat::ReadBool( + input, &packed_)); + _set_bit(1); if (input->ExpectTag(74)) goto parse_experimental_map_key; break; } @@ -4097,13 +4116,18 @@ bool FieldOptions::SerializeWithCachedSizes( DO_(::google::protobuf::internal::WireFormat::WriteEnum(1, this->ctype(), output)); } - // optional string experimental_map_key = 9; + // optional bool packed = 2; if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteBool(2, this->packed(), output)); + } + + // optional string experimental_map_key = 9; + if (_has_bit(2)) { DO_(::google::protobuf::internal::WireFormat::WriteString(9, this->experimental_map_key(), output)); } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - for (int i = 0; i < uninterpreted_option_.size(); i++) { + for (int i = 0; i < this->uninterpreted_option_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(999, this->uninterpreted_option(i), output)); } @@ -4129,6 +4153,11 @@ int FieldOptions::ByteSize() const { ::google::protobuf::internal::WireFormat::EnumSize(this->ctype()); } + // optional bool packed = 2; + if (has_packed()) { + total_size += 1 + 1; + } + // optional string experimental_map_key = 9; if (has_experimental_map_key()) { total_size += 1 + @@ -4137,8 +4166,8 @@ int FieldOptions::ByteSize() const { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - total_size += 2 * uninterpreted_option_size(); - for (int i = 0; i < uninterpreted_option_size(); i++) { + total_size += 2 * this->uninterpreted_option_size(); + for (int i = 0; i < this->uninterpreted_option_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->uninterpreted_option(i)); @@ -4175,6 +4204,9 @@ void FieldOptions::MergeFrom(const FieldOptions& from) { set_ctype(from.ctype()); } if (from._has_bit(1)) { + set_packed(from.packed()); + } + if (from._has_bit(2)) { set_experimental_map_key(from.experimental_map_key()); } } @@ -4197,6 +4229,7 @@ void FieldOptions::CopyFrom(const FieldOptions& from) { void FieldOptions::Swap(FieldOptions* other) { if (other != this) { std::swap(ctype_, other->ctype_); + std::swap(packed_, other->packed_); std::swap(experimental_map_key_, other->experimental_map_key_); uninterpreted_option_.Swap(&other->uninterpreted_option_); std::swap(_has_bits_[0], other->_has_bits_[0]); @@ -4320,7 +4353,7 @@ bool EnumOptions::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { #define DO_(EXPRESSION) if (!(EXPRESSION)) return false // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - for (int i = 0; i < uninterpreted_option_.size(); i++) { + for (int i = 0; i < this->uninterpreted_option_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(999, this->uninterpreted_option(i), output)); } @@ -4340,8 +4373,8 @@ int EnumOptions::ByteSize() const { int total_size = 0; // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - total_size += 2 * uninterpreted_option_size(); - for (int i = 0; i < uninterpreted_option_size(); i++) { + total_size += 2 * this->uninterpreted_option_size(); + for (int i = 0; i < this->uninterpreted_option_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->uninterpreted_option(i)); @@ -4513,7 +4546,7 @@ bool EnumValueOptions::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { #define DO_(EXPRESSION) if (!(EXPRESSION)) return false // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - for (int i = 0; i < uninterpreted_option_.size(); i++) { + for (int i = 0; i < this->uninterpreted_option_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(999, this->uninterpreted_option(i), output)); } @@ -4533,8 +4566,8 @@ int EnumValueOptions::ByteSize() const { int total_size = 0; // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - total_size += 2 * uninterpreted_option_size(); - for (int i = 0; i < uninterpreted_option_size(); i++) { + total_size += 2 * this->uninterpreted_option_size(); + for (int i = 0; i < this->uninterpreted_option_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->uninterpreted_option(i)); @@ -4706,7 +4739,7 @@ bool ServiceOptions::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { #define DO_(EXPRESSION) if (!(EXPRESSION)) return false // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - for (int i = 0; i < uninterpreted_option_.size(); i++) { + for (int i = 0; i < this->uninterpreted_option_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(999, this->uninterpreted_option(i), output)); } @@ -4726,8 +4759,8 @@ int ServiceOptions::ByteSize() const { int total_size = 0; // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - total_size += 2 * uninterpreted_option_size(); - for (int i = 0; i < uninterpreted_option_size(); i++) { + total_size += 2 * this->uninterpreted_option_size(); + for (int i = 0; i < this->uninterpreted_option_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->uninterpreted_option(i)); @@ -4899,7 +4932,7 @@ bool MethodOptions::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { #define DO_(EXPRESSION) if (!(EXPRESSION)) return false // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - for (int i = 0; i < uninterpreted_option_.size(); i++) { + for (int i = 0; i < this->uninterpreted_option_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(999, this->uninterpreted_option(i), output)); } @@ -4919,8 +4952,8 @@ int MethodOptions::ByteSize() const { int total_size = 0; // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - total_size += 2 * uninterpreted_option_size(); - for (int i = 0; i < uninterpreted_option_size(); i++) { + total_size += 2 * this->uninterpreted_option_size(); + for (int i = 0; i < this->uninterpreted_option_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->uninterpreted_option(i)); @@ -5398,7 +5431,7 @@ bool UninterpretedOption::SerializeWithCachedSizes( ::google::protobuf::io::CodedOutputStream* output) const { #define DO_(EXPRESSION) if (!(EXPRESSION)) return false // repeated .google.protobuf.UninterpretedOption.NamePart name = 2; - for (int i = 0; i < name_.size(); i++) { + for (int i = 0; i < this->name_size(); i++) { DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(2, this->name(i), output)); } @@ -5472,8 +5505,8 @@ int UninterpretedOption::ByteSize() const { } // repeated .google.protobuf.UninterpretedOption.NamePart name = 2; - total_size += 1 * name_size(); - for (int i = 0; i < name_size(); i++) { + total_size += 1 * this->name_size(); + for (int i = 0; i < this->name_size(); i++) { total_size += ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( this->name(i)); diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h index 8fa51dd6..e5077aae 100644 --- a/src/google/protobuf/descriptor.pb.h +++ b/src/google/protobuf/descriptor.pb.h @@ -12,7 +12,7 @@ #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif -#if 2000003 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#if 2000004 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. @@ -1645,6 +1645,12 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { inline ::google::protobuf::FieldOptions_CType ctype() const; inline void set_ctype(::google::protobuf::FieldOptions_CType value); + // optional bool packed = 2; + inline bool has_packed() const; + inline void clear_packed(); + inline bool packed() const; + inline void set_packed(bool value); + // optional string experimental_map_key = 9; inline bool has_experimental_map_key() const; inline void clear_experimental_map_key(); @@ -1749,12 +1755,13 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { mutable int _cached_size_; int ctype_; + bool packed_; ::std::string* experimental_map_key_; static const ::std::string _default_experimental_map_key_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto_AssignGlobalDescriptors( const ::google::protobuf::FileDescriptor* file); - ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; + ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? inline bool _has_bit(int index) const { @@ -3908,35 +3915,51 @@ inline void FieldOptions::set_ctype(::google::protobuf::FieldOptions_CType value ctype_ = value; } +// optional bool packed = 2; +inline bool FieldOptions::has_packed() const { + return _has_bit(1); +} +inline void FieldOptions::clear_packed() { + packed_ = false; + _clear_bit(1); +} +inline bool FieldOptions::packed() const { + return packed_; +} +inline void FieldOptions::set_packed(bool value) { + _set_bit(1); + packed_ = value; +} + // optional string experimental_map_key = 9; inline bool FieldOptions::has_experimental_map_key() const { - return _has_bit(1); + return _has_bit(2); } inline void FieldOptions::clear_experimental_map_key() { if (experimental_map_key_ != &_default_experimental_map_key_) { experimental_map_key_->clear(); } - _clear_bit(1); + _clear_bit(2); } inline const ::std::string& FieldOptions::experimental_map_key() const { return *experimental_map_key_; } inline void FieldOptions::set_experimental_map_key(const ::std::string& value) { - _set_bit(1); + _set_bit(2); if (experimental_map_key_ == &_default_experimental_map_key_) { experimental_map_key_ = new ::std::string; } experimental_map_key_->assign(value); } inline void FieldOptions::set_experimental_map_key(const char* value) { - _set_bit(1); + _set_bit(2); if (experimental_map_key_ == &_default_experimental_map_key_) { experimental_map_key_ = new ::std::string; } experimental_map_key_->assign(value); } inline ::std::string* FieldOptions::mutable_experimental_map_key() { - _set_bit(1); + _set_bit(2); if (experimental_map_key_ == &_default_experimental_map_key_) { experimental_map_key_ = new ::std::string; } diff --git a/src/google/protobuf/descriptor.proto b/src/google/protobuf/descriptor.proto index 9cdd61c5..e0e6f7f2 100644 --- a/src/google/protobuf/descriptor.proto +++ b/src/google/protobuf/descriptor.proto @@ -253,6 +253,7 @@ message FileOptions { optional OptimizeMode optimize_for = 9 [default=CODE_SIZE]; + // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; @@ -299,6 +300,11 @@ message FieldOptions { STRING_PIECE = 2; } + // The packed option can be enabled for repeated primitive fields to enable + // a more efficient representation on the wire. Rather than repeatedly + // writing the tag and type for each element, the entire array is encoded as + // a single length-delimited blob. + optional bool packed = 2; // EXPERIMENTAL. DO NOT USE. // For "map" fields, the name of the field in the enclosed type that diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc index f4e60b3a..5ffaea77 100644 --- a/src/google/protobuf/descriptor_unittest.cc +++ b/src/google/protobuf/descriptor_unittest.cc @@ -53,7 +53,8 @@ namespace google { namespace protobuf { -namespace GOOGLE_ANONYMOUS_NAMESPACE{ +// Can't use an anonymous namespace here due to brokenness of Tru64 compiler. +namespace descriptor_unittest { // Some helpers to make assembling descriptors faster. DescriptorProto* AddMessage(FileDescriptorProto* file, const string& name) { @@ -631,6 +632,188 @@ TEST_F(DescriptorTest, FieldEnumType) { // =================================================================== +class StylizedFieldNamesTest : public testing::Test { + protected: + void SetUp() { + FileDescriptorProto file; + file.set_name("foo.proto"); + + AddExtensionRange(AddMessage(&file, "ExtendableMessage"), 1, 1000); + + DescriptorProto* message = AddMessage(&file, "TestMessage"); + AddField(message, "foo_foo", 1, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddField(message, "FooBar", 2, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddField(message, "fooBaz", 3, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddField(message, "fooFoo", 4, // Camel-case conflict with foo_foo. + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddField(message, "foobar", 5, // Lower-case conflict with FooBar. + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + + AddNestedExtension(message, "ExtendableMessage", "bar_foo", 1, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddNestedExtension(message, "ExtendableMessage", "BarBar", 2, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddNestedExtension(message, "ExtendableMessage", "BarBaz", 3, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddNestedExtension(message, "ExtendableMessage", "barFoo", 4, // Conflict + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddNestedExtension(message, "ExtendableMessage", "barbar", 5, // Conflict + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + + AddExtension(&file, "ExtendableMessage", "baz_foo", 11, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddExtension(&file, "ExtendableMessage", "BazBar", 12, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddExtension(&file, "ExtendableMessage", "BazBaz", 13, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddExtension(&file, "ExtendableMessage", "bazFoo", 14, // Conflict + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddExtension(&file, "ExtendableMessage", "bazbar", 15, // Conflict + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + + file_ = pool_.BuildFile(file); + ASSERT_TRUE(file_ != NULL); + ASSERT_EQ(2, file_->message_type_count()); + message_ = file_->message_type(1); + ASSERT_EQ("TestMessage", message_->name()); + ASSERT_EQ(5, message_->field_count()); + ASSERT_EQ(5, message_->extension_count()); + ASSERT_EQ(5, file_->extension_count()); + } + + DescriptorPool pool_; + const FileDescriptor* file_; + const Descriptor* message_; +}; + +TEST_F(StylizedFieldNamesTest, LowercaseName) { + EXPECT_EQ("foo_foo", message_->field(0)->lowercase_name()); + EXPECT_EQ("foobar" , message_->field(1)->lowercase_name()); + EXPECT_EQ("foobaz" , message_->field(2)->lowercase_name()); + EXPECT_EQ("foofoo" , message_->field(3)->lowercase_name()); + EXPECT_EQ("foobar" , message_->field(4)->lowercase_name()); + + EXPECT_EQ("bar_foo", message_->extension(0)->lowercase_name()); + EXPECT_EQ("barbar" , message_->extension(1)->lowercase_name()); + EXPECT_EQ("barbaz" , message_->extension(2)->lowercase_name()); + EXPECT_EQ("barfoo" , message_->extension(3)->lowercase_name()); + EXPECT_EQ("barbar" , message_->extension(4)->lowercase_name()); + + EXPECT_EQ("baz_foo", file_->extension(0)->lowercase_name()); + EXPECT_EQ("bazbar" , file_->extension(1)->lowercase_name()); + EXPECT_EQ("bazbaz" , file_->extension(2)->lowercase_name()); + EXPECT_EQ("bazfoo" , file_->extension(3)->lowercase_name()); + EXPECT_EQ("bazbar" , file_->extension(4)->lowercase_name()); +} + +TEST_F(StylizedFieldNamesTest, CamelcaseName) { + EXPECT_EQ("fooFoo", message_->field(0)->camelcase_name()); + EXPECT_EQ("fooBar", message_->field(1)->camelcase_name()); + EXPECT_EQ("fooBaz", message_->field(2)->camelcase_name()); + EXPECT_EQ("fooFoo", message_->field(3)->camelcase_name()); + EXPECT_EQ("foobar", message_->field(4)->camelcase_name()); + + EXPECT_EQ("barFoo", message_->extension(0)->camelcase_name()); + EXPECT_EQ("barBar", message_->extension(1)->camelcase_name()); + EXPECT_EQ("barBaz", message_->extension(2)->camelcase_name()); + EXPECT_EQ("barFoo", message_->extension(3)->camelcase_name()); + EXPECT_EQ("barbar", message_->extension(4)->camelcase_name()); + + EXPECT_EQ("bazFoo", file_->extension(0)->camelcase_name()); + EXPECT_EQ("bazBar", file_->extension(1)->camelcase_name()); + EXPECT_EQ("bazBaz", file_->extension(2)->camelcase_name()); + EXPECT_EQ("bazFoo", file_->extension(3)->camelcase_name()); + EXPECT_EQ("bazbar", file_->extension(4)->camelcase_name()); +} + +TEST_F(StylizedFieldNamesTest, FindByLowercaseName) { + EXPECT_EQ(message_->field(0), + message_->FindFieldByLowercaseName("foo_foo")); + EXPECT_EQ(message_->field(1), + message_->FindFieldByLowercaseName("foobar")); + EXPECT_EQ(message_->field(2), + message_->FindFieldByLowercaseName("foobaz")); + EXPECT_TRUE(message_->FindFieldByLowercaseName("FooBar") == NULL); + EXPECT_TRUE(message_->FindFieldByLowercaseName("fooBaz") == NULL); + EXPECT_TRUE(message_->FindFieldByLowercaseName("bar_foo") == NULL); + EXPECT_TRUE(message_->FindFieldByLowercaseName("nosuchfield") == NULL); + + EXPECT_EQ(message_->extension(0), + message_->FindExtensionByLowercaseName("bar_foo")); + EXPECT_EQ(message_->extension(1), + message_->FindExtensionByLowercaseName("barbar")); + EXPECT_EQ(message_->extension(2), + message_->FindExtensionByLowercaseName("barbaz")); + EXPECT_TRUE(message_->FindExtensionByLowercaseName("BarBar") == NULL); + EXPECT_TRUE(message_->FindExtensionByLowercaseName("barBaz") == NULL); + EXPECT_TRUE(message_->FindExtensionByLowercaseName("foo_foo") == NULL); + EXPECT_TRUE(message_->FindExtensionByLowercaseName("nosuchfield") == NULL); + + EXPECT_EQ(file_->extension(0), + file_->FindExtensionByLowercaseName("baz_foo")); + EXPECT_EQ(file_->extension(1), + file_->FindExtensionByLowercaseName("bazbar")); + EXPECT_EQ(file_->extension(2), + file_->FindExtensionByLowercaseName("bazbaz")); + EXPECT_TRUE(file_->FindExtensionByLowercaseName("BazBar") == NULL); + EXPECT_TRUE(file_->FindExtensionByLowercaseName("bazBaz") == NULL); + EXPECT_TRUE(file_->FindExtensionByLowercaseName("nosuchfield") == NULL); +} + +TEST_F(StylizedFieldNamesTest, FindByCamelcaseName) { + EXPECT_EQ(message_->field(0), + message_->FindFieldByCamelcaseName("fooFoo")); + EXPECT_EQ(message_->field(1), + message_->FindFieldByCamelcaseName("fooBar")); + EXPECT_EQ(message_->field(2), + message_->FindFieldByCamelcaseName("fooBaz")); + EXPECT_TRUE(message_->FindFieldByCamelcaseName("foo_foo") == NULL); + EXPECT_TRUE(message_->FindFieldByCamelcaseName("FooBar") == NULL); + EXPECT_TRUE(message_->FindFieldByCamelcaseName("barFoo") == NULL); + EXPECT_TRUE(message_->FindFieldByCamelcaseName("nosuchfield") == NULL); + + EXPECT_EQ(message_->extension(0), + message_->FindExtensionByCamelcaseName("barFoo")); + EXPECT_EQ(message_->extension(1), + message_->FindExtensionByCamelcaseName("barBar")); + EXPECT_EQ(message_->extension(2), + message_->FindExtensionByCamelcaseName("barBaz")); + EXPECT_TRUE(message_->FindExtensionByCamelcaseName("bar_foo") == NULL); + EXPECT_TRUE(message_->FindExtensionByCamelcaseName("BarBar") == NULL); + EXPECT_TRUE(message_->FindExtensionByCamelcaseName("fooFoo") == NULL); + EXPECT_TRUE(message_->FindExtensionByCamelcaseName("nosuchfield") == NULL); + + EXPECT_EQ(file_->extension(0), + file_->FindExtensionByCamelcaseName("bazFoo")); + EXPECT_EQ(file_->extension(1), + file_->FindExtensionByCamelcaseName("bazBar")); + EXPECT_EQ(file_->extension(2), + file_->FindExtensionByCamelcaseName("bazBaz")); + EXPECT_TRUE(file_->FindExtensionByCamelcaseName("baz_foo") == NULL); + EXPECT_TRUE(file_->FindExtensionByCamelcaseName("BazBar") == NULL); + EXPECT_TRUE(file_->FindExtensionByCamelcaseName("nosuchfield") == NULL); +} + +// =================================================================== + // Test enum descriptors. class EnumDescriptorTest : public testing::Test { protected: @@ -2457,6 +2640,36 @@ TEST_F(ValidationErrorTest, OutputTypeNotAMessage) { "foo.proto: TestService.A: OUTPUT_TYPE: \"Bar\" is not a message type.\n"); } +TEST_F(ValidationErrorTest, IllegalPackedField) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {\n" + " name: \"Foo\"" + " field { name:\"packed_string\" number:1 label:LABEL_REPEATED " + " type:TYPE_STRING " + " options { uninterpreted_option {" + " name { name_part: \"packed\" is_extension: false }" + " identifier_value: \"true\" }}}\n" + " field { name:\"packed_message\" number:3 label:LABEL_REPEATED " + " type_name: \"Foo\"" + " options { uninterpreted_option {" + " name { name_part: \"packed\" is_extension: false }" + " identifier_value: \"true\" }}}\n" + " field { name:\"optional_int32\" number: 4 label: LABEL_OPTIONAL " + " type:TYPE_INT32 " + " options { uninterpreted_option {" + " name { name_part: \"packed\" is_extension: false }" + " identifier_value: \"true\" }}}\n" + "}", + + "foo.proto: Foo.packed_string: TYPE: [packed = true] can only be " + "specified for repeated primitive fields.\n" + "foo.proto: Foo.packed_message: TYPE: [packed = true] can only be " + "specified for repeated primitive fields.\n" + "foo.proto: Foo.optional_int32: TYPE: [packed = true] can only be " + "specified for repeated primitive fields.\n" + ); +} TEST_F(ValidationErrorTest, OptionWrongType) { BuildFileWithErrors( @@ -3255,6 +3468,34 @@ TEST_F(DatabaseBackedPoolTest, DoesntReloadKnownBadFiles) { EXPECT_EQ("", error_collector.text_); } -} // anonymous namespace +TEST_F(DatabaseBackedPoolTest, DoesntFallbackOnWrongType) { + // If a lookup finds a symbol of the wrong type (e.g. we pass a type name + // to FindFieldByName()), we should fail fast, without checking the fallback + // database. + CallCountingDatabase call_counter(&database_); + DescriptorPool pool(&call_counter); + + const FileDescriptor* file = pool.FindFileByName("foo.proto"); + ASSERT_TRUE(file != NULL); + const Descriptor* foo = pool.FindMessageTypeByName("Foo"); + ASSERT_TRUE(foo != NULL); + const EnumDescriptor* test_enum = pool.FindEnumTypeByName("TestEnum"); + ASSERT_TRUE(test_enum != NULL); + + EXPECT_NE(0, call_counter.call_count_); + call_counter.Clear(); + + EXPECT_TRUE(pool.FindMessageTypeByName("TestEnum") == NULL); + EXPECT_TRUE(pool.FindFieldByName("Foo") == NULL); + EXPECT_TRUE(pool.FindExtensionByName("Foo") == NULL); + EXPECT_TRUE(pool.FindEnumTypeByName("Foo") == NULL); + EXPECT_TRUE(pool.FindEnumValueByName("Foo") == NULL); + EXPECT_TRUE(pool.FindServiceByName("Foo") == NULL); + EXPECT_TRUE(pool.FindMethodByName("Foo") == NULL); + + EXPECT_EQ(0, call_counter.call_count_); +} + +} // namespace descriptor_unittest } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/dynamic_message_unittest.cc b/src/google/protobuf/dynamic_message_unittest.cc index 5f7af94e..41b89ab5 100644 --- a/src/google/protobuf/dynamic_message_unittest.cc +++ b/src/google/protobuf/dynamic_message_unittest.cc @@ -61,6 +61,8 @@ class DynamicMessageTest : public testing::Test { const Message* prototype_; const Descriptor* extensions_descriptor_; const Message* extensions_prototype_; + const Descriptor* packed_descriptor_; + const Message* packed_prototype_; DynamicMessageTest(): factory_(&pool_) {} @@ -87,6 +89,11 @@ class DynamicMessageTest : public testing::Test { pool_.FindMessageTypeByName("protobuf_unittest.TestAllExtensions"); ASSERT_TRUE(extensions_descriptor_ != NULL); extensions_prototype_ = factory_.GetPrototype(extensions_descriptor_); + + packed_descriptor_ = + pool_.FindMessageTypeByName("protobuf_unittest.TestPackedTypes"); + ASSERT_TRUE(packed_descriptor_ != NULL); + packed_prototype_ = factory_.GetPrototype(packed_descriptor_); } }; @@ -127,6 +134,15 @@ TEST_F(DynamicMessageTest, Extensions) { reflection_tester.ExpectAllFieldsSetViaReflection(*message); } +TEST_F(DynamicMessageTest, PackedFields) { + // Check that packed fields work properly. + scoped_ptr<Message> message(packed_prototype_->New()); + TestUtil::ReflectionTester reflection_tester(packed_descriptor_); + + reflection_tester.SetPackedFieldsViaReflection(message.get()); + reflection_tester.ExpectPackedFieldsSetViaReflection(*message); +} + TEST_F(DynamicMessageTest, SpaceUsed) { // Test that SpaceUsed() works properly diff --git a/src/google/protobuf/extension_set_unittest.cc b/src/google/protobuf/extension_set_unittest.cc index c3ac7ce7..631bbc43 100644 --- a/src/google/protobuf/extension_set_unittest.cc +++ b/src/google/protobuf/extension_set_unittest.cc @@ -179,6 +179,19 @@ TEST(ExtensionSetTest, Serialization) { TestUtil::ExpectAllFieldsSet(destination); } +TEST(ExtensionSetTest, PackedSerialization) { + // Serialize as TestPackedExtensions and parse as TestPackedTypes to insure + // wire compatibility of extensions. + unittest::TestPackedExtensions source; + unittest::TestPackedTypes destination; + string data; + + TestUtil::SetPackedExtensions(&source); + source.SerializeToString(&data); + EXPECT_TRUE(destination.ParseFromString(data)); + TestUtil::ExpectPackedFieldsSet(destination); +} + TEST(ExtensionSetTest, Parsing) { // Serialize as TestAllTypes and parse as TestAllExtensions. unittest::TestAllTypes source; @@ -191,6 +204,18 @@ TEST(ExtensionSetTest, Parsing) { TestUtil::ExpectAllExtensionsSet(destination); } +TEST(ExtensionSetTest, PackedParsing) { + // Serialize as TestPackedTypes and parse as TestPackedExtensions. + unittest::TestPackedTypes source; + unittest::TestPackedExtensions destination; + string data; + + TestUtil::SetPackedFields(&source); + source.SerializeToString(&data); + EXPECT_TRUE(destination.ParseFromString(data)); + TestUtil::ExpectPackedExtensionsSet(destination); +} + TEST(ExtensionSetTest, IsInitialized) { // Test that IsInitialized() returns false if required fields in nested // extensions are missing. diff --git a/src/google/protobuf/io/coded_stream.cc b/src/google/protobuf/io/coded_stream.cc index 66f95906..a0f08571 100644 --- a/src/google/protobuf/io/coded_stream.cc +++ b/src/google/protobuf/io/coded_stream.cc @@ -207,6 +207,14 @@ bool CodedInputStream::Skip(int count) { return input_->Skip(count); } +bool CodedInputStream::GetDirectBufferPointer(const void** data, int* size) { + if (buffer_size_ == 0 && !Refresh()) return false; + + *data = buffer_; + *size = buffer_size_; + return true; +} + bool CodedInputStream::ReadRaw(void* buffer, int size) { while (buffer_size_ < size) { // Reading past end of buffer. Copy what we have, then refresh. @@ -515,6 +523,26 @@ CodedOutputStream::~CodedOutputStream() { } } +bool CodedOutputStream::Skip(int count) { + if (count < 0) return false; + + while (count > buffer_size_) { + count -= buffer_size_; + if (!Refresh()) return false; + } + + Advance(count); + return true; +} + +bool CodedOutputStream::GetDirectBufferPointer(void** data, int* size) { + if (buffer_size_ == 0 && !Refresh()) return false; + + *data = buffer_; + *size = buffer_size_; + return true; +} + bool CodedOutputStream::WriteRaw(const void* data, int size) { while (buffer_size_ < size) { memcpy(buffer_, data, buffer_size_); diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h index a73ac0ba..8ebe4b35 100644 --- a/src/google/protobuf/io/coded_stream.h +++ b/src/google/protobuf/io/coded_stream.h @@ -149,6 +149,15 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // occurs. bool Skip(int count); + // Sets *data to point directly at the unread part of the CodedInputStream's + // underlying buffer, and *size to the size of that buffer, but does not + // advance the stream's current position. This will always either produce + // a non-empty buffer or return false. If the caller consumes any of + // this data, it should then call Skip() to skip over the consumed bytes. + // This may be useful for implementing external fast parsing routines for + // types of data not covered by the CodedInputStream interface. + bool GetDirectBufferPointer(const void** data, int* size); + // Read raw bytes, copying them into the given buffer. bool ReadRaw(void* buffer, int size); @@ -381,6 +390,21 @@ class LIBPROTOBUF_EXPORT CodedOutputStream { // ZeroCopyOutputStream immediately after the last byte written. ~CodedOutputStream(); + // Skips a number of bytes, leaving the bytes unmodified in the underlying + // buffer. Returns false if an underlying write error occurs. This is + // mainly useful with GetDirectBufferPointer(). + bool Skip(int count); + + // Sets *data to point directly at the unwritten part of the + // CodedOutputStream's underlying buffer, and *size to the size of that + // buffer, but does not advance the stream's current position. This will + // always either produce a non-empty buffer or return false. If the caller + // writes any data to this buffer, it should then call Skip() to skip over + // the consumed bytes. This may be useful for implementing external fast + // serialization routines for types of data not covered by the + // CodedOutputStream interface. + bool GetDirectBufferPointer(void** data, int* size); + // Write raw bytes, copying them from the given buffer. bool WriteRaw(const void* buffer, int size); @@ -518,7 +542,7 @@ inline bool CodedInputStream::ExpectAtEnd() { inline bool CodedOutputStream::WriteVarint32(uint32 value) { if (value < 0x80 && buffer_size_ > 0) { - *buffer_ = value; + *buffer_ = static_cast<uint8>(value); Advance(1); return true; } else { @@ -537,7 +561,7 @@ inline bool CodedOutputStream::WriteVarint32SignExtended(int32 value) { inline bool CodedOutputStream::WriteTag(uint32 value) { if (value < (1 << 7)) { if (buffer_size_ != 0) { - buffer_[0] = value; + buffer_[0] = static_cast<uint8>(value); Advance(1); return true; } diff --git a/src/google/protobuf/io/coded_stream_unittest.cc b/src/google/protobuf/io/coded_stream_unittest.cc index 459b94a6..6a6eafe9 100644 --- a/src/google/protobuf/io/coded_stream_unittest.cc +++ b/src/google/protobuf/io/coded_stream_unittest.cc @@ -77,6 +77,9 @@ namespace { // which failed will be printed. The case type must be printable using // ostream::operator<<. +// TODO(kenton): gTest now supports "parameterized tests" which would be +// a better way to accomplish this. Rewrite when time permits. + #define TEST_1D(FIXTURE, NAME, CASES) \ class FIXTURE##_##NAME##_DD : public FIXTURE { \ protected: \ @@ -614,6 +617,73 @@ TEST_1D(CodedStreamTest, SkipInput, kBlockSizes) { } // ------------------------------------------------------------------- +// GetDirectBufferPointer + +TEST_F(CodedStreamTest, GetDirectBufferPointerInput) { + ArrayInputStream input(buffer_, sizeof(buffer_), 8); + CodedInputStream coded_input(&input); + + const void* ptr; + int size; + + EXPECT_TRUE(coded_input.GetDirectBufferPointer(&ptr, &size)); + EXPECT_EQ(buffer_, ptr); + EXPECT_EQ(8, size); + + // Peeking again should return the same pointer. + EXPECT_TRUE(coded_input.GetDirectBufferPointer(&ptr, &size)); + EXPECT_EQ(buffer_, ptr); + EXPECT_EQ(8, size); + + // Skip forward in the same buffer then peek again. + EXPECT_TRUE(coded_input.Skip(3)); + EXPECT_TRUE(coded_input.GetDirectBufferPointer(&ptr, &size)); + EXPECT_EQ(buffer_ + 3, ptr); + EXPECT_EQ(5, size); + + // Skip to end of buffer and peek -- should get next buffer. + EXPECT_TRUE(coded_input.Skip(5)); + EXPECT_TRUE(coded_input.GetDirectBufferPointer(&ptr, &size)); + EXPECT_EQ(buffer_ + 8, ptr); + EXPECT_EQ(8, size); +} + +TEST_F(CodedStreamTest, GetDirectBufferPointerOutput) { + ArrayOutputStream output(buffer_, sizeof(buffer_), 8); + CodedOutputStream coded_output(&output); + + void* ptr; + int size; + + EXPECT_TRUE(coded_output.GetDirectBufferPointer(&ptr, &size)); + EXPECT_EQ(buffer_, ptr); + EXPECT_EQ(8, size); + + // Peeking again should return the same pointer. + EXPECT_TRUE(coded_output.GetDirectBufferPointer(&ptr, &size)); + EXPECT_EQ(buffer_, ptr); + EXPECT_EQ(8, size); + + // Skip forward in the same buffer then peek again. + EXPECT_TRUE(coded_output.Skip(3)); + EXPECT_TRUE(coded_output.GetDirectBufferPointer(&ptr, &size)); + EXPECT_EQ(buffer_ + 3, ptr); + EXPECT_EQ(5, size); + + // Skip to end of buffer and peek -- should get next buffer. + EXPECT_TRUE(coded_output.Skip(5)); + EXPECT_TRUE(coded_output.GetDirectBufferPointer(&ptr, &size)); + EXPECT_EQ(buffer_ + 8, ptr); + EXPECT_EQ(8, size); + + // Skip over multiple buffers. + EXPECT_TRUE(coded_output.Skip(22)); + EXPECT_TRUE(coded_output.GetDirectBufferPointer(&ptr, &size)); + EXPECT_EQ(buffer_ + 30, ptr); + EXPECT_EQ(2, size); +} + +// ------------------------------------------------------------------- // Limits TEST_1D(CodedStreamTest, BasicLimit, kBlockSizes) { diff --git a/src/google/protobuf/message.cc b/src/google/protobuf/message.cc index f6c932ff..097411cb 100644 --- a/src/google/protobuf/message.cc +++ b/src/google/protobuf/message.cc @@ -145,24 +145,42 @@ bool Message::ParsePartialFromZeroCopyStream(io::ZeroCopyInputStream* input) { decoder.ConsumedEntireMessage(); } +bool Message::ParseFromBoundedZeroCopyStream( + io::ZeroCopyInputStream* input, int size) { + io::CodedInputStream decoder(input); + decoder.PushLimit(size); + return ParseFromCodedStream(&decoder) && + decoder.ConsumedEntireMessage() && + decoder.BytesUntilLimit() == 0; +} + +bool Message::ParsePartialFromBoundedZeroCopyStream( + io::ZeroCopyInputStream* input, int size) { + io::CodedInputStream decoder(input); + decoder.PushLimit(size); + return ParsePartialFromCodedStream(&decoder) && + decoder.ConsumedEntireMessage() && + decoder.BytesUntilLimit() == 0; +} + bool Message::ParseFromString(const string& data) { io::ArrayInputStream input(data.data(), data.size()); - return ParseFromZeroCopyStream(&input); + return ParseFromBoundedZeroCopyStream(&input, data.size()); } bool Message::ParsePartialFromString(const string& data) { io::ArrayInputStream input(data.data(), data.size()); - return ParsePartialFromZeroCopyStream(&input); + return ParsePartialFromBoundedZeroCopyStream(&input, data.size()); } bool Message::ParseFromArray(const void* data, int size) { io::ArrayInputStream input(data, size); - return ParseFromZeroCopyStream(&input); + return ParseFromBoundedZeroCopyStream(&input, size); } bool Message::ParsePartialFromArray(const void* data, int size) { io::ArrayInputStream input(data, size); - return ParsePartialFromZeroCopyStream(&input); + return ParsePartialFromBoundedZeroCopyStream(&input, size); } bool Message::ParseFromFileDescriptor(int file_descriptor) { diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h index d96fcc60..0674a12c 100644 --- a/src/google/protobuf/message.h +++ b/src/google/protobuf/message.h @@ -232,6 +232,14 @@ class LIBPROTOBUF_EXPORT Message { // Like ParseFromZeroCopyStream(), but accepts messages that are missing // required fields. bool ParsePartialFromZeroCopyStream(io::ZeroCopyInputStream* input); + // Read a protocol buffer from the given zero-copy input stream, expecting + // the message to be exactly "size" bytes long. If successful, exactly + // this many bytes will have been consumed from the input. + bool ParseFromBoundedZeroCopyStream(io::ZeroCopyInputStream* input, int size); + // Like ParseFromBoundedZeroCopyStream(), but accepts messages that are + // missing required fields. + bool ParsePartialFromBoundedZeroCopyStream(io::ZeroCopyInputStream* input, + int size); // Parse a protocol buffer contained in a string. bool ParseFromString(const string& data); // Like ParseFromString(), but accepts messages that are missing diff --git a/src/google/protobuf/message_unittest.cc b/src/google/protobuf/message_unittest.cc index da99741b..46e68446 100644 --- a/src/google/protobuf/message_unittest.cc +++ b/src/google/protobuf/message_unittest.cc @@ -107,6 +107,19 @@ TEST(MessageTest, ParseFromFileDescriptor) { EXPECT_GE(close(file), 0); } +TEST(MessageTest, ParsePackedFromFileDescriptor) { + string filename = + TestSourceDir() + + "/google/protobuf/testdata/golden_packed_fields_message"; + int file = open(filename.c_str(), O_RDONLY | O_BINARY); + + unittest::TestPackedTypes message; + EXPECT_TRUE(message.ParseFromFileDescriptor(file)); + TestUtil::ExpectPackedFieldsSet(message); + + EXPECT_GE(close(file), 0); +} + TEST(MessageTest, ParseHelpers) { // TODO(kenton): Test more helpers? They're all two-liners so it seems // like a waste of time. @@ -134,6 +147,25 @@ TEST(MessageTest, ParseHelpers) { EXPECT_TRUE(stream.eof()); TestUtil::ExpectAllFieldsSet(message); } + + { + // Test ParseFromBoundedZeroCopyStream. + string data_with_junk(data); + data_with_junk.append("some junk on the end"); + io::ArrayInputStream stream(data_with_junk.data(), data_with_junk.size()); + protobuf_unittest::TestAllTypes message; + EXPECT_TRUE(message.ParseFromBoundedZeroCopyStream(&stream, data.size())); + TestUtil::ExpectAllFieldsSet(message); + } + + { + // Test that ParseFromBoundedZeroCopyStream fails (but doesn't crash) if + // EOF is reached before the expected number of bytes. + io::ArrayInputStream stream(data.data(), data.size()); + protobuf_unittest::TestAllTypes message; + EXPECT_FALSE( + message.ParseFromBoundedZeroCopyStream(&stream, data.size() + 1)); + } } TEST(MessageTest, ParseFailsIfNotInitialized) { diff --git a/src/google/protobuf/test_util.cc b/src/google/protobuf/test_util.cc index 40120d95..c1e9fa78 100644 --- a/src/google/protobuf/test_util.cc +++ b/src/google/protobuf/test_util.cc @@ -636,6 +636,180 @@ void TestUtil::ExpectRepeatedFieldsModified( } +// ------------------------------------------------------------------- + +void TestUtil::SetPackedFields(unittest::TestPackedTypes* message) { + message->add_packed_int32 (601); + message->add_packed_int64 (602); + message->add_packed_uint32 (603); + message->add_packed_uint64 (604); + message->add_packed_sint32 (605); + message->add_packed_sint64 (606); + message->add_packed_fixed32 (607); + message->add_packed_fixed64 (608); + message->add_packed_sfixed32(609); + message->add_packed_sfixed64(610); + message->add_packed_float (611); + message->add_packed_double (612); + message->add_packed_bool (true); + message->add_packed_enum (unittest::FOREIGN_BAR); + // add a second one of each field + message->add_packed_int32 (701); + message->add_packed_int64 (702); + message->add_packed_uint32 (703); + message->add_packed_uint64 (704); + message->add_packed_sint32 (705); + message->add_packed_sint64 (706); + message->add_packed_fixed32 (707); + message->add_packed_fixed64 (708); + message->add_packed_sfixed32(709); + message->add_packed_sfixed64(710); + message->add_packed_float (711); + message->add_packed_double (712); + message->add_packed_bool (false); + message->add_packed_enum (unittest::FOREIGN_BAZ); +} + +// ------------------------------------------------------------------- + +void TestUtil::ModifyPackedFields(unittest::TestPackedTypes* message) { + message->set_packed_int32 (1, 801); + message->set_packed_int64 (1, 802); + message->set_packed_uint32 (1, 803); + message->set_packed_uint64 (1, 804); + message->set_packed_sint32 (1, 805); + message->set_packed_sint64 (1, 806); + message->set_packed_fixed32 (1, 807); + message->set_packed_fixed64 (1, 808); + message->set_packed_sfixed32(1, 809); + message->set_packed_sfixed64(1, 810); + message->set_packed_float (1, 811); + message->set_packed_double (1, 812); + message->set_packed_bool (1, true); + message->set_packed_enum (1, unittest::FOREIGN_FOO); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectPackedFieldsSet(const unittest::TestPackedTypes& message) { + ASSERT_EQ(2, message.packed_int32_size ()); + ASSERT_EQ(2, message.packed_int64_size ()); + ASSERT_EQ(2, message.packed_uint32_size ()); + ASSERT_EQ(2, message.packed_uint64_size ()); + ASSERT_EQ(2, message.packed_sint32_size ()); + ASSERT_EQ(2, message.packed_sint64_size ()); + ASSERT_EQ(2, message.packed_fixed32_size ()); + ASSERT_EQ(2, message.packed_fixed64_size ()); + ASSERT_EQ(2, message.packed_sfixed32_size()); + ASSERT_EQ(2, message.packed_sfixed64_size()); + ASSERT_EQ(2, message.packed_float_size ()); + ASSERT_EQ(2, message.packed_double_size ()); + ASSERT_EQ(2, message.packed_bool_size ()); + ASSERT_EQ(2, message.packed_enum_size ()); + + EXPECT_EQ(601 , message.packed_int32 (0)); + EXPECT_EQ(602 , message.packed_int64 (0)); + EXPECT_EQ(603 , message.packed_uint32 (0)); + EXPECT_EQ(604 , message.packed_uint64 (0)); + EXPECT_EQ(605 , message.packed_sint32 (0)); + EXPECT_EQ(606 , message.packed_sint64 (0)); + EXPECT_EQ(607 , message.packed_fixed32 (0)); + EXPECT_EQ(608 , message.packed_fixed64 (0)); + EXPECT_EQ(609 , message.packed_sfixed32(0)); + EXPECT_EQ(610 , message.packed_sfixed64(0)); + EXPECT_EQ(611 , message.packed_float (0)); + EXPECT_EQ(612 , message.packed_double (0)); + EXPECT_EQ(true , message.packed_bool (0)); + EXPECT_EQ(unittest::FOREIGN_BAR, message.packed_enum(0)); + + EXPECT_EQ(701 , message.packed_int32 (1)); + EXPECT_EQ(702 , message.packed_int64 (1)); + EXPECT_EQ(703 , message.packed_uint32 (1)); + EXPECT_EQ(704 , message.packed_uint64 (1)); + EXPECT_EQ(705 , message.packed_sint32 (1)); + EXPECT_EQ(706 , message.packed_sint64 (1)); + EXPECT_EQ(707 , message.packed_fixed32 (1)); + EXPECT_EQ(708 , message.packed_fixed64 (1)); + EXPECT_EQ(709 , message.packed_sfixed32(1)); + EXPECT_EQ(710 , message.packed_sfixed64(1)); + EXPECT_EQ(711 , message.packed_float (1)); + EXPECT_EQ(712 , message.packed_double (1)); + EXPECT_EQ(false, message.packed_bool (1)); + EXPECT_EQ(unittest::FOREIGN_BAZ, message.packed_enum(1)); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectPackedClear( + const unittest::TestPackedTypes& message) { + // Packed repeated fields are empty. + EXPECT_EQ(0, message.packed_int32_size ()); + EXPECT_EQ(0, message.packed_int64_size ()); + EXPECT_EQ(0, message.packed_uint32_size ()); + EXPECT_EQ(0, message.packed_uint64_size ()); + EXPECT_EQ(0, message.packed_sint32_size ()); + EXPECT_EQ(0, message.packed_sint64_size ()); + EXPECT_EQ(0, message.packed_fixed32_size ()); + EXPECT_EQ(0, message.packed_fixed64_size ()); + EXPECT_EQ(0, message.packed_sfixed32_size()); + EXPECT_EQ(0, message.packed_sfixed64_size()); + EXPECT_EQ(0, message.packed_float_size ()); + EXPECT_EQ(0, message.packed_double_size ()); + EXPECT_EQ(0, message.packed_bool_size ()); + EXPECT_EQ(0, message.packed_enum_size ()); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectPackedFieldsModified( + const unittest::TestPackedTypes& message) { + // Do the same for packed repeated fields. + ASSERT_EQ(2, message.packed_int32_size ()); + ASSERT_EQ(2, message.packed_int64_size ()); + ASSERT_EQ(2, message.packed_uint32_size ()); + ASSERT_EQ(2, message.packed_uint64_size ()); + ASSERT_EQ(2, message.packed_sint32_size ()); + ASSERT_EQ(2, message.packed_sint64_size ()); + ASSERT_EQ(2, message.packed_fixed32_size ()); + ASSERT_EQ(2, message.packed_fixed64_size ()); + ASSERT_EQ(2, message.packed_sfixed32_size()); + ASSERT_EQ(2, message.packed_sfixed64_size()); + ASSERT_EQ(2, message.packed_float_size ()); + ASSERT_EQ(2, message.packed_double_size ()); + ASSERT_EQ(2, message.packed_bool_size ()); + ASSERT_EQ(2, message.packed_enum_size ()); + + EXPECT_EQ(601 , message.packed_int32 (0)); + EXPECT_EQ(602 , message.packed_int64 (0)); + EXPECT_EQ(603 , message.packed_uint32 (0)); + EXPECT_EQ(604 , message.packed_uint64 (0)); + EXPECT_EQ(605 , message.packed_sint32 (0)); + EXPECT_EQ(606 , message.packed_sint64 (0)); + EXPECT_EQ(607 , message.packed_fixed32 (0)); + EXPECT_EQ(608 , message.packed_fixed64 (0)); + EXPECT_EQ(609 , message.packed_sfixed32(0)); + EXPECT_EQ(610 , message.packed_sfixed64(0)); + EXPECT_EQ(611 , message.packed_float (0)); + EXPECT_EQ(612 , message.packed_double (0)); + EXPECT_EQ(true , message.packed_bool (0)); + EXPECT_EQ(unittest::FOREIGN_BAR, message.packed_enum(0)); + // Actually verify the second (modified) elements now. + EXPECT_EQ(801 , message.packed_int32 (1)); + EXPECT_EQ(802 , message.packed_int64 (1)); + EXPECT_EQ(803 , message.packed_uint32 (1)); + EXPECT_EQ(804 , message.packed_uint64 (1)); + EXPECT_EQ(805 , message.packed_sint32 (1)); + EXPECT_EQ(806 , message.packed_sint64 (1)); + EXPECT_EQ(807 , message.packed_fixed32 (1)); + EXPECT_EQ(808 , message.packed_fixed64 (1)); + EXPECT_EQ(809 , message.packed_sfixed32(1)); + EXPECT_EQ(810 , message.packed_sfixed64(1)); + EXPECT_EQ(811 , message.packed_float (1)); + EXPECT_EQ(812 , message.packed_double (1)); + EXPECT_EQ(true , message.packed_bool (1)); + EXPECT_EQ(unittest::FOREIGN_FOO, message.packed_enum(1)); +} + // =================================================================== // Extensions // @@ -1246,6 +1420,183 @@ void TestUtil::ExpectRepeatedExtensionsModified( // ------------------------------------------------------------------- +void TestUtil::SetPackedExtensions(unittest::TestPackedExtensions* message) { + message->AddExtension(unittest::packed_int32_extension , 601); + message->AddExtension(unittest::packed_int64_extension , 602); + message->AddExtension(unittest::packed_uint32_extension , 603); + message->AddExtension(unittest::packed_uint64_extension , 604); + message->AddExtension(unittest::packed_sint32_extension , 605); + message->AddExtension(unittest::packed_sint64_extension , 606); + message->AddExtension(unittest::packed_fixed32_extension , 607); + message->AddExtension(unittest::packed_fixed64_extension , 608); + message->AddExtension(unittest::packed_sfixed32_extension, 609); + message->AddExtension(unittest::packed_sfixed64_extension, 610); + message->AddExtension(unittest::packed_float_extension , 611); + message->AddExtension(unittest::packed_double_extension , 612); + message->AddExtension(unittest::packed_bool_extension , true); + message->AddExtension(unittest::packed_enum_extension, unittest::FOREIGN_BAR); + // add a second one of each field + message->AddExtension(unittest::packed_int32_extension , 701); + message->AddExtension(unittest::packed_int64_extension , 702); + message->AddExtension(unittest::packed_uint32_extension , 703); + message->AddExtension(unittest::packed_uint64_extension , 704); + message->AddExtension(unittest::packed_sint32_extension , 705); + message->AddExtension(unittest::packed_sint64_extension , 706); + message->AddExtension(unittest::packed_fixed32_extension , 707); + message->AddExtension(unittest::packed_fixed64_extension , 708); + message->AddExtension(unittest::packed_sfixed32_extension, 709); + message->AddExtension(unittest::packed_sfixed64_extension, 710); + message->AddExtension(unittest::packed_float_extension , 711); + message->AddExtension(unittest::packed_double_extension , 712); + message->AddExtension(unittest::packed_bool_extension , false); + message->AddExtension(unittest::packed_enum_extension, unittest::FOREIGN_BAZ); +} + +// ------------------------------------------------------------------- + +void TestUtil::ModifyPackedExtensions(unittest::TestPackedExtensions* message) { + message->SetExtension(unittest::packed_int32_extension , 1, 801); + message->SetExtension(unittest::packed_int64_extension , 1, 802); + message->SetExtension(unittest::packed_uint32_extension , 1, 803); + message->SetExtension(unittest::packed_uint64_extension , 1, 804); + message->SetExtension(unittest::packed_sint32_extension , 1, 805); + message->SetExtension(unittest::packed_sint64_extension , 1, 806); + message->SetExtension(unittest::packed_fixed32_extension , 1, 807); + message->SetExtension(unittest::packed_fixed64_extension , 1, 808); + message->SetExtension(unittest::packed_sfixed32_extension, 1, 809); + message->SetExtension(unittest::packed_sfixed64_extension, 1, 810); + message->SetExtension(unittest::packed_float_extension , 1, 811); + message->SetExtension(unittest::packed_double_extension , 1, 812); + message->SetExtension(unittest::packed_bool_extension , 1, true); + message->SetExtension(unittest::packed_enum_extension , 1, + unittest::FOREIGN_FOO); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectPackedExtensionsSet( + const unittest::TestPackedExtensions& message) { + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_int32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_int64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_uint32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_uint64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_sint32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_sint64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_fixed32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_fixed64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_sfixed32_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_sfixed64_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_float_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_double_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_bool_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_enum_extension )); + + EXPECT_EQ(601 , message.GetExtension(unittest::packed_int32_extension , 0)); + EXPECT_EQ(602 , message.GetExtension(unittest::packed_int64_extension , 0)); + EXPECT_EQ(603 , message.GetExtension(unittest::packed_uint32_extension , 0)); + EXPECT_EQ(604 , message.GetExtension(unittest::packed_uint64_extension , 0)); + EXPECT_EQ(605 , message.GetExtension(unittest::packed_sint32_extension , 0)); + EXPECT_EQ(606 , message.GetExtension(unittest::packed_sint64_extension , 0)); + EXPECT_EQ(607 , message.GetExtension(unittest::packed_fixed32_extension , 0)); + EXPECT_EQ(608 , message.GetExtension(unittest::packed_fixed64_extension , 0)); + EXPECT_EQ(609 , message.GetExtension(unittest::packed_sfixed32_extension, 0)); + EXPECT_EQ(610 , message.GetExtension(unittest::packed_sfixed64_extension, 0)); + EXPECT_EQ(611 , message.GetExtension(unittest::packed_float_extension , 0)); + EXPECT_EQ(612 , message.GetExtension(unittest::packed_double_extension , 0)); + EXPECT_EQ(true , message.GetExtension(unittest::packed_bool_extension , 0)); + EXPECT_EQ(unittest::FOREIGN_BAR, + message.GetExtension(unittest::packed_enum_extension, 0)); + EXPECT_EQ(701 , message.GetExtension(unittest::packed_int32_extension , 1)); + EXPECT_EQ(702 , message.GetExtension(unittest::packed_int64_extension , 1)); + EXPECT_EQ(703 , message.GetExtension(unittest::packed_uint32_extension , 1)); + EXPECT_EQ(704 , message.GetExtension(unittest::packed_uint64_extension , 1)); + EXPECT_EQ(705 , message.GetExtension(unittest::packed_sint32_extension , 1)); + EXPECT_EQ(706 , message.GetExtension(unittest::packed_sint64_extension , 1)); + EXPECT_EQ(707 , message.GetExtension(unittest::packed_fixed32_extension , 1)); + EXPECT_EQ(708 , message.GetExtension(unittest::packed_fixed64_extension , 1)); + EXPECT_EQ(709 , message.GetExtension(unittest::packed_sfixed32_extension, 1)); + EXPECT_EQ(710 , message.GetExtension(unittest::packed_sfixed64_extension, 1)); + EXPECT_EQ(711 , message.GetExtension(unittest::packed_float_extension , 1)); + EXPECT_EQ(712 , message.GetExtension(unittest::packed_double_extension , 1)); + EXPECT_EQ(false, message.GetExtension(unittest::packed_bool_extension , 1)); + EXPECT_EQ(unittest::FOREIGN_BAZ, + message.GetExtension(unittest::packed_enum_extension, 1)); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectPackedExtensionsClear( + const unittest::TestPackedExtensions& message) { + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_int32_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_int64_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_uint32_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_uint64_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_sint32_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_sint64_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_fixed32_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_fixed64_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_sfixed32_extension)); + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_sfixed64_extension)); + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_float_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_double_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_bool_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::packed_enum_extension )); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectPackedExtensionsModified( + const unittest::TestPackedExtensions& message) { + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_int32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_int64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_uint32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_uint64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_sint32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_sint64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_fixed32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_fixed64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_sfixed32_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_sfixed64_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_float_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_double_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_bool_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::packed_enum_extension )); + EXPECT_EQ(601 , message.GetExtension(unittest::packed_int32_extension , 0)); + EXPECT_EQ(602 , message.GetExtension(unittest::packed_int64_extension , 0)); + EXPECT_EQ(603 , message.GetExtension(unittest::packed_uint32_extension , 0)); + EXPECT_EQ(604 , message.GetExtension(unittest::packed_uint64_extension , 0)); + EXPECT_EQ(605 , message.GetExtension(unittest::packed_sint32_extension , 0)); + EXPECT_EQ(606 , message.GetExtension(unittest::packed_sint64_extension , 0)); + EXPECT_EQ(607 , message.GetExtension(unittest::packed_fixed32_extension , 0)); + EXPECT_EQ(608 , message.GetExtension(unittest::packed_fixed64_extension , 0)); + EXPECT_EQ(609 , message.GetExtension(unittest::packed_sfixed32_extension, 0)); + EXPECT_EQ(610 , message.GetExtension(unittest::packed_sfixed64_extension, 0)); + EXPECT_EQ(611 , message.GetExtension(unittest::packed_float_extension , 0)); + EXPECT_EQ(612 , message.GetExtension(unittest::packed_double_extension , 0)); + EXPECT_EQ(true , message.GetExtension(unittest::packed_bool_extension , 0)); + EXPECT_EQ(unittest::FOREIGN_BAR, + message.GetExtension(unittest::packed_enum_extension, 0)); + + // Actually verify the second (modified) elements now. + EXPECT_EQ(801 , message.GetExtension(unittest::packed_int32_extension , 1)); + EXPECT_EQ(802 , message.GetExtension(unittest::packed_int64_extension , 1)); + EXPECT_EQ(803 , message.GetExtension(unittest::packed_uint32_extension , 1)); + EXPECT_EQ(804 , message.GetExtension(unittest::packed_uint64_extension , 1)); + EXPECT_EQ(805 , message.GetExtension(unittest::packed_sint32_extension , 1)); + EXPECT_EQ(806 , message.GetExtension(unittest::packed_sint64_extension , 1)); + EXPECT_EQ(807 , message.GetExtension(unittest::packed_fixed32_extension , 1)); + EXPECT_EQ(808 , message.GetExtension(unittest::packed_fixed64_extension , 1)); + EXPECT_EQ(809 , message.GetExtension(unittest::packed_sfixed32_extension, 1)); + EXPECT_EQ(810 , message.GetExtension(unittest::packed_sfixed64_extension, 1)); + EXPECT_EQ(811 , message.GetExtension(unittest::packed_float_extension , 1)); + EXPECT_EQ(812 , message.GetExtension(unittest::packed_double_extension , 1)); + EXPECT_EQ(true , message.GetExtension(unittest::packed_bool_extension , 1)); + EXPECT_EQ(unittest::FOREIGN_FOO, + message.GetExtension(unittest::packed_enum_extension, 1)); +} + +// ------------------------------------------------------------------- + void TestUtil::ExpectAllFieldsAndExtensionsInOrder(const string& serialized) { // We set each field individually, serialize separately, and concatenate all // the strings in canonical order to determine the expected serialization. @@ -1335,7 +1686,8 @@ TestUtil::ReflectionTester::ReflectionTester( // Shorthand to get a FieldDescriptor for a field of unittest::TestAllTypes. const FieldDescriptor* TestUtil::ReflectionTester::F(const string& name) { const FieldDescriptor* result = NULL; - if (base_descriptor_->name() == "TestAllExtensions") { + if (base_descriptor_->name() == "TestAllExtensions" || + base_descriptor_->name() == "TestPackedExtensions") { result = base_descriptor_->file()->FindExtensionByName(name + "_extension"); } else { result = base_descriptor_->FindFieldByName(name); @@ -1475,6 +1827,40 @@ void TestUtil::ReflectionTester::SetAllFieldsViaReflection(Message* message) { reflection->SetString(message, F("default_cord"), "425"); } +void TestUtil::ReflectionTester::SetPackedFieldsViaReflection( + Message* message) { + const Reflection* reflection = message->GetReflection(); + reflection->AddInt32 (message, F("packed_int32" ), 601); + reflection->AddInt64 (message, F("packed_int64" ), 602); + reflection->AddUInt32(message, F("packed_uint32" ), 603); + reflection->AddUInt64(message, F("packed_uint64" ), 604); + reflection->AddInt32 (message, F("packed_sint32" ), 605); + reflection->AddInt64 (message, F("packed_sint64" ), 606); + reflection->AddUInt32(message, F("packed_fixed32" ), 607); + reflection->AddUInt64(message, F("packed_fixed64" ), 608); + reflection->AddInt32 (message, F("packed_sfixed32"), 609); + reflection->AddInt64 (message, F("packed_sfixed64"), 610); + reflection->AddFloat (message, F("packed_float" ), 611); + reflection->AddDouble(message, F("packed_double" ), 612); + reflection->AddBool (message, F("packed_bool" ), true); + reflection->AddEnum (message, F("packed_enum" ), foreign_bar_); + + reflection->AddInt32 (message, F("packed_int32" ), 701); + reflection->AddInt64 (message, F("packed_int64" ), 702); + reflection->AddUInt32(message, F("packed_uint32" ), 703); + reflection->AddUInt64(message, F("packed_uint64" ), 704); + reflection->AddInt32 (message, F("packed_sint32" ), 705); + reflection->AddInt64 (message, F("packed_sint64" ), 706); + reflection->AddUInt32(message, F("packed_fixed32" ), 707); + reflection->AddUInt64(message, F("packed_fixed64" ), 708); + reflection->AddInt32 (message, F("packed_sfixed32"), 709); + reflection->AddInt64 (message, F("packed_sfixed64"), 710); + reflection->AddFloat (message, F("packed_float" ), 711); + reflection->AddDouble(message, F("packed_double" ), 712); + reflection->AddBool (message, F("packed_bool" ), false); + reflection->AddEnum (message, F("packed_enum" ), foreign_baz_); +} + // ------------------------------------------------------------------- void TestUtil::ReflectionTester::ExpectAllFieldsSetViaReflection( @@ -1725,6 +2111,58 @@ void TestUtil::ReflectionTester::ExpectAllFieldsSetViaReflection( EXPECT_EQ("425", reflection->GetStringReference(message, F("default_cord"), &scratch)); } +void TestUtil::ReflectionTester::ExpectPackedFieldsSetViaReflection( + const Message& message) { + const Reflection* reflection = message.GetReflection(); + + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_int32" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_int64" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_uint32" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_uint64" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_sint32" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_sint64" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_fixed32" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_fixed64" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_sfixed32"))); + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_sfixed64"))); + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_float" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_double" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_bool" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("packed_enum" ))); + + EXPECT_EQ(601 , reflection->GetRepeatedInt32 (message, F("packed_int32" ), 0)); + EXPECT_EQ(602 , reflection->GetRepeatedInt64 (message, F("packed_int64" ), 0)); + EXPECT_EQ(603 , reflection->GetRepeatedUInt32(message, F("packed_uint32" ), 0)); + EXPECT_EQ(604 , reflection->GetRepeatedUInt64(message, F("packed_uint64" ), 0)); + EXPECT_EQ(605 , reflection->GetRepeatedInt32 (message, F("packed_sint32" ), 0)); + EXPECT_EQ(606 , reflection->GetRepeatedInt64 (message, F("packed_sint64" ), 0)); + EXPECT_EQ(607 , reflection->GetRepeatedUInt32(message, F("packed_fixed32" ), 0)); + EXPECT_EQ(608 , reflection->GetRepeatedUInt64(message, F("packed_fixed64" ), 0)); + EXPECT_EQ(609 , reflection->GetRepeatedInt32 (message, F("packed_sfixed32"), 0)); + EXPECT_EQ(610 , reflection->GetRepeatedInt64 (message, F("packed_sfixed64"), 0)); + EXPECT_EQ(611 , reflection->GetRepeatedFloat (message, F("packed_float" ), 0)); + EXPECT_EQ(612 , reflection->GetRepeatedDouble(message, F("packed_double" ), 0)); + EXPECT_EQ(true , reflection->GetRepeatedBool (message, F("packed_bool" ), 0)); + EXPECT_EQ(foreign_bar_, + reflection->GetRepeatedEnum(message, F("packed_enum"), 0)); + + EXPECT_EQ(701 , reflection->GetRepeatedInt32 (message, F("packed_int32" ), 1)); + EXPECT_EQ(702 , reflection->GetRepeatedInt64 (message, F("packed_int64" ), 1)); + EXPECT_EQ(703 , reflection->GetRepeatedUInt32(message, F("packed_uint32" ), 1)); + EXPECT_EQ(704 , reflection->GetRepeatedUInt64(message, F("packed_uint64" ), 1)); + EXPECT_EQ(705 , reflection->GetRepeatedInt32 (message, F("packed_sint32" ), 1)); + EXPECT_EQ(706 , reflection->GetRepeatedInt64 (message, F("packed_sint64" ), 1)); + EXPECT_EQ(707 , reflection->GetRepeatedUInt32(message, F("packed_fixed32" ), 1)); + EXPECT_EQ(708 , reflection->GetRepeatedUInt64(message, F("packed_fixed64" ), 1)); + EXPECT_EQ(709 , reflection->GetRepeatedInt32 (message, F("packed_sfixed32"), 1)); + EXPECT_EQ(710 , reflection->GetRepeatedInt64 (message, F("packed_sfixed64"), 1)); + EXPECT_EQ(711 , reflection->GetRepeatedFloat (message, F("packed_float" ), 1)); + EXPECT_EQ(712 , reflection->GetRepeatedDouble(message, F("packed_double" ), 1)); + EXPECT_EQ(false, reflection->GetRepeatedBool (message, F("packed_bool" ), 1)); + EXPECT_EQ(foreign_baz_, + reflection->GetRepeatedEnum(message, F("packed_enum"), 1)); +} + // ------------------------------------------------------------------- void TestUtil::ReflectionTester::ExpectClearViaReflection( @@ -1890,6 +2328,26 @@ void TestUtil::ReflectionTester::ExpectClearViaReflection( EXPECT_EQ("123", reflection->GetStringReference(message, F("default_cord"), &scratch)); } +void TestUtil::ReflectionTester::ExpectPackedClearViaReflection( + const Message& message) { + const Reflection* reflection = message.GetReflection(); + + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_int32" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_int64" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_uint32" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_uint64" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_sint32" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_sint64" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_fixed32" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_fixed64" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_sfixed32"))); + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_sfixed64"))); + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_float" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_double" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_bool" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("packed_enum" ))); +} + // ------------------------------------------------------------------- void TestUtil::ReflectionTester::ModifyRepeatedFieldsViaReflection( @@ -1930,5 +2388,24 @@ void TestUtil::ReflectionTester::ModifyRepeatedFieldsViaReflection( reflection->SetRepeatedString(message, F("repeated_cord"), 1, "525"); } +void TestUtil::ReflectionTester::ModifyPackedFieldsViaReflection( + Message* message) { + const Reflection* reflection = message->GetReflection(); + reflection->SetRepeatedInt32 (message, F("packed_int32" ), 1, 801); + reflection->SetRepeatedInt64 (message, F("packed_int64" ), 1, 802); + reflection->SetRepeatedUInt32(message, F("packed_uint32" ), 1, 803); + reflection->SetRepeatedUInt64(message, F("packed_uint64" ), 1, 804); + reflection->SetRepeatedInt32 (message, F("packed_sint32" ), 1, 805); + reflection->SetRepeatedInt64 (message, F("packed_sint64" ), 1, 806); + reflection->SetRepeatedUInt32(message, F("packed_fixed32" ), 1, 807); + reflection->SetRepeatedUInt64(message, F("packed_fixed64" ), 1, 808); + reflection->SetRepeatedInt32 (message, F("packed_sfixed32"), 1, 809); + reflection->SetRepeatedInt64 (message, F("packed_sfixed64"), 1, 810); + reflection->SetRepeatedFloat (message, F("packed_float" ), 1, 811); + reflection->SetRepeatedDouble(message, F("packed_double" ), 1, 812); + reflection->SetRepeatedBool (message, F("packed_bool" ), 1, true); + reflection->SetRepeatedEnum (message, F("packed_enum" ), 1, foreign_foo_); +} + } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/test_util.h b/src/google/protobuf/test_util.h index af03afc3..ca840c71 100644 --- a/src/google/protobuf/test_util.h +++ b/src/google/protobuf/test_util.h @@ -52,30 +52,44 @@ class TestUtil { static void SetAllFields(unittest::TestAllTypes* message); static void SetAllExtensions(unittest::TestAllExtensions* message); static void SetAllFieldsAndExtensions(unittest::TestFieldOrderings* message); + static void SetPackedFields(unittest::TestPackedTypes* message); + static void SetPackedExtensions(unittest::TestPackedExtensions* message); // Use the repeated versions of the set_*() accessors to modify all the // repeated fields of the messsage (which should already have been - // initialized with SetAllFields()). SetAllFields() itself only tests + // initialized with Set*Fields()). Set*Fields() itself only tests // the add_*() accessors. static void ModifyRepeatedFields(unittest::TestAllTypes* message); static void ModifyRepeatedExtensions(unittest::TestAllExtensions* message); + static void ModifyPackedFields(unittest::TestPackedTypes* message); + static void ModifyPackedExtensions(unittest::TestPackedExtensions* message); // Check that all fields have the values that they should have after - // SetAllFields() is called. + // Set*Fields() is called. static void ExpectAllFieldsSet(const unittest::TestAllTypes& message); static void ExpectAllExtensionsSet( const unittest::TestAllExtensions& message); + static void ExpectPackedFieldsSet(const unittest::TestPackedTypes& message); + static void ExpectPackedExtensionsSet( + const unittest::TestPackedExtensions& message); // Expect that the message is modified as would be expected from - // ModifyRepeatedFields(). + // Modify*Fields(). static void ExpectRepeatedFieldsModified( const unittest::TestAllTypes& message); static void ExpectRepeatedExtensionsModified( const unittest::TestAllExtensions& message); + static void ExpectPackedFieldsModified( + const unittest::TestPackedTypes& message); + static void ExpectPackedExtensionsModified( + const unittest::TestPackedExtensions& message); // Check that all fields have their default values. static void ExpectClear(const unittest::TestAllTypes& message); static void ExpectExtensionsClear(const unittest::TestAllExtensions& message); + static void ExpectPackedClear(const unittest::TestPackedTypes& message); + static void ExpectPackedExtensionsClear( + const unittest::TestPackedExtensions& message); // Check that the passed-in serialization is the canonical serialization we // expect for a TestFieldOrderings message filled in by @@ -97,6 +111,11 @@ class TestUtil { void ExpectAllFieldsSetViaReflection(const Message& message); void ExpectClearViaReflection(const Message& message); + void SetPackedFieldsViaReflection(Message* message); + void ModifyPackedFieldsViaReflection(Message* message); + void ExpectPackedFieldsSetViaReflection(const Message& message); + void ExpectPackedClearViaReflection(const Message& message); + private: const FieldDescriptor* F(const string& name); diff --git a/src/google/protobuf/unittest.proto b/src/google/protobuf/unittest.proto index 0aa66b9f..46ce77c3 100644 --- a/src/google/protobuf/unittest.proto +++ b/src/google/protobuf/unittest.proto @@ -461,6 +461,46 @@ message OneBytes { optional bytes data = 1; } +// Test messages for packed fields + +message TestPackedTypes { + repeated int32 packed_int32 = 90 [packed = true]; + repeated int64 packed_int64 = 91 [packed = true]; + repeated uint32 packed_uint32 = 92 [packed = true]; + repeated uint64 packed_uint64 = 93 [packed = true]; + repeated sint32 packed_sint32 = 94 [packed = true]; + repeated sint64 packed_sint64 = 95 [packed = true]; + repeated fixed32 packed_fixed32 = 96 [packed = true]; + repeated fixed64 packed_fixed64 = 97 [packed = true]; + repeated sfixed32 packed_sfixed32 = 98 [packed = true]; + repeated sfixed64 packed_sfixed64 = 99 [packed = true]; + repeated float packed_float = 100 [packed = true]; + repeated double packed_double = 101 [packed = true]; + repeated bool packed_bool = 102 [packed = true]; + repeated ForeignEnum packed_enum = 103 [packed = true]; +} + +message TestPackedExtensions { + extensions 1 to max; +} + +extend TestPackedExtensions { + repeated int32 packed_int32_extension = 90 [packed = true]; + repeated int64 packed_int64_extension = 91 [packed = true]; + repeated uint32 packed_uint32_extension = 92 [packed = true]; + repeated uint64 packed_uint64_extension = 93 [packed = true]; + repeated sint32 packed_sint32_extension = 94 [packed = true]; + repeated sint64 packed_sint64_extension = 95 [packed = true]; + repeated fixed32 packed_fixed32_extension = 96 [packed = true]; + repeated fixed64 packed_fixed64_extension = 97 [packed = true]; + repeated sfixed32 packed_sfixed32_extension = 98 [packed = true]; + repeated sfixed64 packed_sfixed64_extension = 99 [packed = true]; + repeated float packed_float_extension = 100 [packed = true]; + repeated double packed_double_extension = 101 [packed = true]; + repeated bool packed_bool_extension = 102 [packed = true]; + repeated ForeignEnum packed_enum_extension = 103 [packed = true]; +} + // Test that RPC services work. message FooRequest {} message FooResponse {} diff --git a/src/google/protobuf/unknown_field_set.cc b/src/google/protobuf/unknown_field_set.cc index 6a9be5a4..f42f9a59 100644 --- a/src/google/protobuf/unknown_field_set.cc +++ b/src/google/protobuf/unknown_field_set.cc @@ -154,6 +154,46 @@ int UnknownFieldSet::SpaceUsed() const { return sizeof(*this) + SpaceUsedExcludingSelf(); } +UnknownFieldSet::Internal::FieldMap UnknownFieldSet::kEmptyMap; +const UnknownFieldSet::iterator UnknownFieldSet::kEmptyIterator( + kEmptyMap.end(), &kEmptyMap); +const UnknownFieldSet::const_iterator UnknownFieldSet::kEmptyConstIterator( + kEmptyMap.end(), &kEmptyMap); + +void UnknownFieldSet::iterator::AdvanceToNonEmpty() { + while (inner_iterator_ != inner_map_->end() && + (inner_iterator_->second->index() == -1 || + inner_iterator_->second->empty())) { + ++inner_iterator_; + } +} + +void UnknownFieldSet::const_iterator::AdvanceToNonEmpty() { + while (inner_iterator_ != inner_map_->end() && + (inner_iterator_->second->index() == -1 || + inner_iterator_->second->empty())) { + ++inner_iterator_; + } +} + +UnknownFieldSet::iterator UnknownFieldSet::begin() { + if (internal_ == NULL) return kEmptyIterator; + + UnknownFieldSet::iterator result(internal_->fields_.begin(), + &internal_->fields_); + result.AdvanceToNonEmpty(); + return result; +} + +UnknownFieldSet::const_iterator UnknownFieldSet::begin() const { + if (internal_ == NULL) return kEmptyIterator; + + UnknownFieldSet::const_iterator result(internal_->fields_.begin(), + &internal_->fields_); + result.AdvanceToNonEmpty(); + return result; +} + UnknownField::UnknownField(int number) : number_(number), index_(-1) { diff --git a/src/google/protobuf/unknown_field_set.h b/src/google/protobuf/unknown_field_set.h index 55eec6e2..f17012df 100644 --- a/src/google/protobuf/unknown_field_set.h +++ b/src/google/protobuf/unknown_field_set.h @@ -70,6 +70,13 @@ class LIBPROTOBUF_EXPORT UnknownFieldSet { void Clear(); // Is this set empty? + // + // Note that this is equivalent to field_count() == 0 but is NOT necessarily + // equivalent to begin() == end(). The iterator class skips fields which are + // themselves empty, so if field_count() is non-zero but field(i)->empty() is + // true for all i, then begin() will be equal to end() but empty() will return + // false. This inconsistency almost never occurs in practice because typical + // code does not add empty fields to an UnknownFieldSet. inline bool empty() const; // Merge the contents of some other UnknownFieldSet with this one. @@ -78,6 +85,117 @@ class LIBPROTOBUF_EXPORT UnknownFieldSet { // Swaps the contents of some other UnknownFieldSet with this one. inline void Swap(UnknownFieldSet* x); + // Find a field by field number. Returns NULL if not found. + const UnknownField* FindFieldByNumber(int number) const; + + // Add a field by field number. If the field number already exists, returns + // the existing UnknownField. + UnknownField* AddField(int number); + + // Computes (an estimate of) the total number of bytes currently used for + // storing the unknown fields in memory. Does NOT include + // sizeof(*this) in the calculation. + int SpaceUsedExcludingSelf() const; + + // Version of SpaceUsed() including sizeof(*this). + int SpaceUsed() const; + + // STL-style iteration --------------------------------------------- + // These iterate over the non-empty UnknownFields in order by field + // number. All iterators are invalidated whenever the UnknownFieldSet + // is modified. + + class const_iterator; + + class LIBPROTOBUF_EXPORT iterator { + public: + iterator() {} + + bool operator==(const iterator& other) { + return inner_iterator_ == other.inner_iterator_; + } + bool operator!=(const iterator& other) { + return inner_iterator_ != other.inner_iterator_; + } + + UnknownField& operator*() { return *inner_iterator_->second; } + UnknownField* operator->() { return inner_iterator_->second; } + iterator& operator++() { + ++inner_iterator_; + AdvanceToNonEmpty(); + return *this; + } + iterator operator++(int) { + iterator copy(*this); + ++*this; + return copy; + } + + private: + friend class UnknownFieldSet; + friend class LIBPROTOBUF_EXPORT UnknownFieldSet::const_iterator; + iterator(map<int, UnknownField*>::iterator inner_iterator, + map<int, UnknownField*>* inner_map) + : inner_iterator_(inner_iterator), inner_map_(inner_map) {} + + void AdvanceToNonEmpty(); + + map<int, UnknownField*>::iterator inner_iterator_; + map<int, UnknownField*>* inner_map_; + }; + + class LIBPROTOBUF_EXPORT const_iterator { + public: + const_iterator() {} + const_iterator(const iterator& other) + : inner_iterator_(other.inner_iterator_), inner_map_(other.inner_map_) {} + + bool operator==(const const_iterator& other) { + return inner_iterator_ == other.inner_iterator_; + } + bool operator!=(const const_iterator& other) { + return inner_iterator_ != other.inner_iterator_; + } + + UnknownField& operator*() { return *inner_iterator_->second; } + UnknownField* operator->() { return inner_iterator_->second; } + const_iterator& operator++() { + ++inner_iterator_; + AdvanceToNonEmpty(); + return *this; + } + const_iterator operator++(int) { + const_iterator copy(*this); + ++*this; + return copy; + } + + private: + friend class UnknownFieldSet; + const_iterator(map<int, UnknownField*>::const_iterator inner_iterator, + const map<int, UnknownField*>* inner_map) + : inner_iterator_(inner_iterator), inner_map_(inner_map) {} + + void AdvanceToNonEmpty(); + + map<int, UnknownField*>::const_iterator inner_iterator_; + const map<int, UnknownField*>* inner_map_; + }; + + iterator begin(); + iterator end() { + return internal_ == NULL ? kEmptyIterator : + iterator(internal_->fields_.end(), &internal_->fields_); + } + const_iterator begin() const; + const_iterator end() const { + return internal_ == NULL ? kEmptyConstIterator : + const_iterator(internal_->fields_.end(), &internal_->fields_); + } + + // Old-style iteration --------------------------------------------- + // New code should use begin() and end() rather than these methods. + // Returns the number of fields present in the UnknownFieldSet. inline int field_count() const; // Get a field in the set, where 0 <= index < field_count(). The fields @@ -87,13 +205,6 @@ class LIBPROTOBUF_EXPORT UnknownFieldSet { // 0 <= index < field_count(). The fields appear in arbitrary order. inline UnknownField* mutable_field(int index); - // Find a field by field number. Returns NULL if not found. - const UnknownField* FindFieldByNumber(int number) const; - - // Add a field by field number. If the field number already exists, returns - // the existing UnknownField. - UnknownField* AddField(int number); - // Parsing helpers ------------------------------------------------- // These work exactly like the similarly-named methods of Message. @@ -105,13 +216,6 @@ class LIBPROTOBUF_EXPORT UnknownFieldSet { return ParseFromArray(data.data(), data.size()); } - // Computes (an estimate of) the total number of bytes currently used for - // storing the unknown fields in memory. Does NOT include - // sizeof(*this) in the calculation. - int SpaceUsedExcludingSelf() const; - // Version of SpaceUsed() including sizeof(*this). - int SpaceUsed() const; - private: // "Active" fields are ones which have been added since the last time Clear() // was called. Inactive fields are objects we are keeping around incase @@ -139,6 +243,11 @@ class LIBPROTOBUF_EXPORT UnknownFieldSet { // Don't keep more inactive fields than this. static const int kMaxInactiveFields = 100; + // Used by begin() and end() when internal_ is NULL. + static Internal::FieldMap kEmptyMap; + static const iterator kEmptyIterator; + static const const_iterator kEmptyConstIterator; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UnknownFieldSet); }; @@ -160,6 +269,9 @@ class LIBPROTOBUF_EXPORT UnknownField { // Clears all fields. void Clear(); + // Is this field empty? (I.e. all of the *_size() methods return zero.) + inline bool empty() const; + // Merge the contents of some other UnknownField with this one. For each // wire type, the values are simply concatenated. void MergeFrom(const UnknownField& other); @@ -256,6 +368,14 @@ inline UnknownField* UnknownFieldSet::mutable_field(int index) { return internal_->active_fields_[index]; } +inline bool UnknownField::empty() const { + return varint_.size() == 0 && + fixed32_.size() == 0 && + fixed64_.size() == 0 && + length_delimited_.size() == 0 && + group_.size() == 0; +} + inline int UnknownField::number() const { return number_; } inline int UnknownField::index () const { return index_; } @@ -322,8 +442,8 @@ inline UnknownFieldSet* UnknownField::add_group() { } inline void UnknownField::clear_varint () { varint_.Clear(); } -inline void UnknownField::clear_fixed32() { varint_.Clear(); } -inline void UnknownField::clear_fixed64() { varint_.Clear(); } +inline void UnknownField::clear_fixed32() { fixed32_.Clear(); } +inline void UnknownField::clear_fixed64() { fixed64_.Clear(); } inline void UnknownField::clear_length_delimited() { length_delimited_.Clear(); } diff --git a/src/google/protobuf/unknown_field_set_unittest.cc b/src/google/protobuf/unknown_field_set_unittest.cc index c7e78b23..0a75af9f 100644 --- a/src/google/protobuf/unknown_field_set_unittest.cc +++ b/src/google/protobuf/unknown_field_set_unittest.cc @@ -516,6 +516,92 @@ TEST_F(UnknownFieldSetTest, SpaceUsed) { EXPECT_EQ(expected_size, unknown_fields.SpaceUsed()); } +TEST_F(UnknownFieldSetTest, Empty) { + UnknownFieldSet unknown_fields; + EXPECT_TRUE(unknown_fields.empty()); + unknown_fields.AddField(6)->add_varint(123); + EXPECT_FALSE(unknown_fields.empty()); + unknown_fields.Clear(); + EXPECT_TRUE(unknown_fields.empty()); +} + +TEST_F(UnknownFieldSetTest, FieldEmpty) { + UnknownFieldSet unknown_fields; + UnknownField* field = unknown_fields.AddField(1); + + EXPECT_TRUE(field->empty()); + + field->add_varint(1); + EXPECT_FALSE(field->empty()); + field->Clear(); + EXPECT_TRUE(field->empty()); + + field->add_fixed32(1); + EXPECT_FALSE(field->empty()); + field->Clear(); + EXPECT_TRUE(field->empty()); + + field->add_fixed64(1); + EXPECT_FALSE(field->empty()); + field->Clear(); + EXPECT_TRUE(field->empty()); + + field->add_length_delimited("foo"); + EXPECT_FALSE(field->empty()); + field->Clear(); + EXPECT_TRUE(field->empty()); + + field->add_group(); + EXPECT_FALSE(field->empty()); + field->Clear(); + EXPECT_TRUE(field->empty()); +} + +TEST_F(UnknownFieldSetTest, Iterator) { + UnknownFieldSet unknown_fields; + EXPECT_TRUE(unknown_fields.begin() == unknown_fields.end()); + + // Populate the UnknownFieldSet with some inactive fields by adding some + // fields and then clearing. + unknown_fields.AddField(6); + unknown_fields.AddField(4); + unknown_fields.Clear(); + + // Add a bunch of "active" fields. + UnknownField* a = unknown_fields.AddField(5); + unknown_fields.AddField(3); + unknown_fields.AddField(9); + unknown_fields.AddField(1); + UnknownField* b = unknown_fields.AddField(2); + + // Only make some of them non-empty. + a->add_varint(1); + b->add_length_delimited("foo"); + + // Iterate! + { + UnknownFieldSet::iterator iter = unknown_fields.begin(); + ASSERT_TRUE(iter != unknown_fields.end()); + EXPECT_EQ(b, &*iter); + ++iter; + ASSERT_TRUE(iter != unknown_fields.end()); + EXPECT_EQ(a, &*iter); + ++iter; + EXPECT_TRUE(iter == unknown_fields.end()); + } + + { + UnknownFieldSet::const_iterator iter = unknown_fields.begin(); + ASSERT_TRUE(iter != unknown_fields.end()); + EXPECT_EQ(b, &*iter); + ++iter; + ASSERT_TRUE(iter != unknown_fields.end()); + EXPECT_EQ(a, &*iter); + ++iter; + EXPECT_TRUE(iter == unknown_fields.end()); + } +} + } // namespace } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/wire_format.cc b/src/google/protobuf/wire_format.cc index 99ea619b..aeea3ccb 100644 --- a/src/google/protobuf/wire_format.cc +++ b/src/google/protobuf/wire_format.cc @@ -369,93 +369,155 @@ bool WireFormat::ParseAndMergeField( const Reflection* message_reflection = message->GetReflection(); if (field == NULL || - GetTagWireType(tag) != WireTypeForFieldType(field->type())) { + GetTagWireType(tag) != WireTypeForField(field)) { // We don't recognize this field. Either the field number is unknown // or the wire type doesn't match. Put it in our unknown field set. return SkipField(input, tag, message_reflection->MutableUnknownFields(message)); } - switch (field->type()) { -#define HANDLE_TYPE(TYPE, TYPE_METHOD, CPPTYPE, CPPTYPE_METHOD) \ - case FieldDescriptor::TYPE_##TYPE: { \ - CPPTYPE value; \ - if (!Read##TYPE_METHOD(input, &value)) return false; \ - if (field->is_repeated()) { \ - message_reflection->Add##CPPTYPE_METHOD(message, field, value); \ - } else { \ - message_reflection->Set##CPPTYPE_METHOD(message, field, value); \ - } \ - break; \ - } - - HANDLE_TYPE( INT32, Int32, int32, Int32) - HANDLE_TYPE( INT64, Int64, int64, Int64) - HANDLE_TYPE(SINT32, SInt32, int32, Int32) - HANDLE_TYPE(SINT64, SInt64, int64, Int64) - HANDLE_TYPE(UINT32, UInt32, uint32, UInt32) - HANDLE_TYPE(UINT64, UInt64, uint64, UInt64) - - HANDLE_TYPE( FIXED32, Fixed32, uint32, UInt32) - HANDLE_TYPE( FIXED64, Fixed64, uint64, UInt64) - HANDLE_TYPE(SFIXED32, SFixed32, int32, Int32) - HANDLE_TYPE(SFIXED64, SFixed64, int64, Int64) - - HANDLE_TYPE(FLOAT , Float , float , Float ) - HANDLE_TYPE(DOUBLE, Double, double, Double) - - HANDLE_TYPE(BOOL, Bool, bool, Bool) - - HANDLE_TYPE(STRING, String, string, String) - HANDLE_TYPE(BYTES, Bytes, string, String) + if (field->options().packed()) { + uint32 length; + if (!input->ReadVarint32(&length)) return false; + io::CodedInputStream::Limit limit = input->PushLimit(length); -#undef HANDLE_TYPE + switch (field->type()) { +#define HANDLE_PACKED_TYPE(TYPE, TYPE_METHOD, CPPTYPE, CPPTYPE_METHOD) \ + case FieldDescriptor::TYPE_##TYPE: { \ + while (input->BytesUntilLimit() > 0) { \ + CPPTYPE value; \ + if (!Read##TYPE_METHOD(input, &value)) return false; \ + message_reflection->Add##CPPTYPE_METHOD(message, field, value); \ + } \ + break; \ + } - case FieldDescriptor::TYPE_ENUM: { - int value; - if (!ReadEnum(input, &value)) return false; - const EnumValueDescriptor* enum_value = - field->enum_type()->FindValueByNumber(value); - if (enum_value != NULL) { - if (field->is_repeated()) { - message_reflection->AddEnum(message, field, enum_value); - } else { - message_reflection->SetEnum(message, field, enum_value); + HANDLE_PACKED_TYPE( INT32, Int32, int32, Int32) + HANDLE_PACKED_TYPE( INT64, Int64, int64, Int64) + HANDLE_PACKED_TYPE(SINT32, SInt32, int32, Int32) + HANDLE_PACKED_TYPE(SINT64, SInt64, int64, Int64) + HANDLE_PACKED_TYPE(UINT32, UInt32, uint32, UInt32) + HANDLE_PACKED_TYPE(UINT64, UInt64, uint64, UInt64) + + HANDLE_PACKED_TYPE( FIXED32, Fixed32, uint32, UInt32) + HANDLE_PACKED_TYPE( FIXED64, Fixed64, uint64, UInt64) + HANDLE_PACKED_TYPE(SFIXED32, SFixed32, int32, Int32) + HANDLE_PACKED_TYPE(SFIXED64, SFixed64, int64, Int64) + + HANDLE_PACKED_TYPE(FLOAT , Float , float , Float ) + HANDLE_PACKED_TYPE(DOUBLE, Double, double, Double) + + HANDLE_PACKED_TYPE(BOOL, Bool, bool, Bool) +#undef HANDLE_PACKED_TYPE + + case FieldDescriptor::TYPE_ENUM: { + while (input->BytesUntilLimit() > 0) { + int value; + if (!ReadEnum(input, &value)) return false; + const EnumValueDescriptor* enum_value = + field->enum_type()->FindValueByNumber(value); + if (enum_value != NULL) { + message_reflection->AddEnum(message, field, enum_value); + } } - } else { - // The enum value is not one of the known values. Add it to the - // UnknownFieldSet. - int64 sign_extended_value = static_cast<int64>(value); - message_reflection->MutableUnknownFields(message) - ->AddField(GetTagFieldNumber(tag)) - ->add_varint(sign_extended_value); + + break; } - break; + + case FieldDescriptor::TYPE_STRING: + case FieldDescriptor::TYPE_GROUP: + case FieldDescriptor::TYPE_MESSAGE: + case FieldDescriptor::TYPE_BYTES: + // Can't have packed fields of these types: these should be caught by + // the protocol compiler. + return false; + break; } + input->PopLimit(limit); + } else { + switch (field->type()) { +#define HANDLE_TYPE(TYPE, TYPE_METHOD, CPPTYPE, CPPTYPE_METHOD) \ + case FieldDescriptor::TYPE_##TYPE: { \ + CPPTYPE value; \ + if (!Read##TYPE_METHOD(input, &value)) return false; \ + if (field->is_repeated()) { \ + message_reflection->Add##CPPTYPE_METHOD(message, field, value); \ + } else { \ + message_reflection->Set##CPPTYPE_METHOD(message, field, value); \ + } \ + break; \ + } + + HANDLE_TYPE( INT32, Int32, int32, Int32) + HANDLE_TYPE( INT64, Int64, int64, Int64) + HANDLE_TYPE(SINT32, SInt32, int32, Int32) + HANDLE_TYPE(SINT64, SInt64, int64, Int64) + HANDLE_TYPE(UINT32, UInt32, uint32, UInt32) + HANDLE_TYPE(UINT64, UInt64, uint64, UInt64) - case FieldDescriptor::TYPE_GROUP: { - Message* sub_message; - if (field->is_repeated()) { - sub_message = message_reflection->AddMessage(message, field); - } else { - sub_message = message_reflection->MutableMessage(message, field); + HANDLE_TYPE( FIXED32, Fixed32, uint32, UInt32) + HANDLE_TYPE( FIXED64, Fixed64, uint64, UInt64) + HANDLE_TYPE(SFIXED32, SFixed32, int32, Int32) + HANDLE_TYPE(SFIXED64, SFixed64, int64, Int64) + + HANDLE_TYPE(FLOAT , Float , float , Float ) + HANDLE_TYPE(DOUBLE, Double, double, Double) + + HANDLE_TYPE(BOOL, Bool, bool, Bool) + + HANDLE_TYPE(STRING, String, string, String) + HANDLE_TYPE(BYTES, Bytes, string, String) + +#undef HANDLE_TYPE + + case FieldDescriptor::TYPE_ENUM: { + int value; + if (!ReadEnum(input, &value)) return false; + const EnumValueDescriptor* enum_value = + field->enum_type()->FindValueByNumber(value); + if (enum_value != NULL) { + if (field->is_repeated()) { + message_reflection->AddEnum(message, field, enum_value); + } else { + message_reflection->SetEnum(message, field, enum_value); + } + } else { + // The enum value is not one of the known values. Add it to the + // UnknownFieldSet. + int64 sign_extended_value = static_cast<int64>(value); + message_reflection->MutableUnknownFields(message) + ->AddField(GetTagFieldNumber(tag)) + ->add_varint(sign_extended_value); + } + break; } - if (!ReadGroup(GetTagFieldNumber(tag), input, sub_message)) return false; - break; - } - case FieldDescriptor::TYPE_MESSAGE: { - Message* sub_message; - if (field->is_repeated()) { - sub_message = message_reflection->AddMessage(message, field); - } else { - sub_message = message_reflection->MutableMessage(message, field); + case FieldDescriptor::TYPE_GROUP: { + Message* sub_message; + if (field->is_repeated()) { + sub_message = message_reflection->AddMessage(message, field); + } else { + sub_message = message_reflection->MutableMessage(message, field); + } + + if (!ReadGroup(GetTagFieldNumber(tag), input, sub_message)) + return false; + break; } - if (!ReadMessage(input, sub_message)) return false; - break; + case FieldDescriptor::TYPE_MESSAGE: { + Message* sub_message; + if (field->is_repeated()) { + sub_message = message_reflection->AddMessage(message, field); + } else { + sub_message = message_reflection->MutableMessage(message, field); + } + + if (!ReadMessage(input, sub_message)) return false; + break; + } } } @@ -602,8 +664,53 @@ bool WireFormat::SerializeFieldWithCachedSizes( count = 1; } + const bool is_packed = field->options().packed(); + if (is_packed && count > 0) { + if (!WriteTag(field->number(), WIRETYPE_LENGTH_DELIMITED, output)) + return false; + const int data_size = FieldDataOnlyByteSize(field, message); + if (!output->WriteVarint32(data_size)) return false; + } + for (int j = 0; j < count; j++) { switch (field->type()) { +#define HANDLE_PRIMITIVE_TYPE(TYPE, CPPTYPE, TYPE_METHOD, CPPTYPE_METHOD) \ + case FieldDescriptor::TYPE_##TYPE: { \ + const CPPTYPE value = field->is_repeated() ? \ + message_reflection->GetRepeated##CPPTYPE_METHOD( \ + message, field, j) : \ + message_reflection->Get##CPPTYPE_METHOD( \ + message, field); \ + if (is_packed) { \ + if (!Write##TYPE_METHOD##NoTag(value, output)) { \ + return false; \ + } \ + } else { \ + if (!Write##TYPE_METHOD(field->number(), value, output)) { \ + return false; \ + } \ + } \ + break; \ + } + + HANDLE_PRIMITIVE_TYPE( INT32, int32, Int32, Int32) + HANDLE_PRIMITIVE_TYPE( INT64, int64, Int64, Int64) + HANDLE_PRIMITIVE_TYPE(SINT32, int32, SInt32, Int32) + HANDLE_PRIMITIVE_TYPE(SINT64, int64, SInt64, Int64) + HANDLE_PRIMITIVE_TYPE(UINT32, uint32, UInt32, UInt32) + HANDLE_PRIMITIVE_TYPE(UINT64, uint64, UInt64, UInt64) + + HANDLE_PRIMITIVE_TYPE( FIXED32, uint32, Fixed32, UInt32) + HANDLE_PRIMITIVE_TYPE( FIXED64, uint64, Fixed64, UInt64) + HANDLE_PRIMITIVE_TYPE(SFIXED32, int32, SFixed32, Int32) + HANDLE_PRIMITIVE_TYPE(SFIXED64, int64, SFixed64, Int64) + + HANDLE_PRIMITIVE_TYPE(FLOAT , float , Float , Float ) + HANDLE_PRIMITIVE_TYPE(DOUBLE, double, Double, Double) + + HANDLE_PRIMITIVE_TYPE(BOOL, bool, Bool, Bool) +#undef HANDLE_PRIMITIVE_TYPE + #define HANDLE_TYPE(TYPE, TYPE_METHOD, CPPTYPE_METHOD) \ case FieldDescriptor::TYPE_##TYPE: \ if (!Write##TYPE_METHOD( \ @@ -617,23 +724,6 @@ bool WireFormat::SerializeFieldWithCachedSizes( } \ break; - HANDLE_TYPE( INT32, Int32, Int32) - HANDLE_TYPE( INT64, Int64, Int64) - HANDLE_TYPE(SINT32, SInt32, Int32) - HANDLE_TYPE(SINT64, SInt64, Int64) - HANDLE_TYPE(UINT32, UInt32, UInt32) - HANDLE_TYPE(UINT64, UInt64, UInt64) - - HANDLE_TYPE( FIXED32, Fixed32, UInt32) - HANDLE_TYPE( FIXED64, Fixed64, UInt64) - HANDLE_TYPE(SFIXED32, SFixed32, Int32) - HANDLE_TYPE(SFIXED64, SFixed64, Int64) - - HANDLE_TYPE(FLOAT , Float , Float ) - HANDLE_TYPE(DOUBLE, Double, Double) - - HANDLE_TYPE(BOOL, Bool, Bool) - HANDLE_TYPE(GROUP , Group , Message) HANDLE_TYPE(MESSAGE, Message, Message) #undef HANDLE_TYPE @@ -642,7 +732,12 @@ bool WireFormat::SerializeFieldWithCachedSizes( const EnumValueDescriptor* value = field->is_repeated() ? message_reflection->GetRepeatedEnum(message, field, j) : message_reflection->GetEnum(message, field); - if (!WriteEnum(field->number(), value->number(), output)) return false; + if (is_packed) { + if (!WriteEnumNoTag(value->number(), output)) return false; + } else { + if (!WriteEnum(field->number(), value->number(), output)) + return false; + } break; } @@ -736,36 +831,60 @@ int WireFormat::FieldByteSize( return MessageSetItemByteSize(field, message); } - int our_size = 0; - int count = 0; - if (field->is_repeated()) { count = message_reflection->FieldSize(message, field); } else if (message_reflection->HasField(message, field)) { count = 1; } - our_size += count * TagSize(field->number(), field->type()); + const int data_size = FieldDataOnlyByteSize(field, message); + int our_size = data_size; + if (field->options().packed()) { + if (data_size > 0) { + // Packed fields get serialized like a string, not their native type. + // Technically this doesn't really matter; the size only changes if it's + // a GROUP + our_size += TagSize(field->number(), FieldDescriptor::TYPE_STRING); + our_size += io::CodedOutputStream::VarintSize32(data_size); + } + } else { + our_size += count * TagSize(field->number(), field->type()); + } + return our_size; +} + +int WireFormat::FieldDataOnlyByteSize( + const FieldDescriptor* field, + const Message& message) { + const Reflection* message_reflection = message.GetReflection(); + + int count = 0; + if (field->is_repeated()) { + count = message_reflection->FieldSize(message, field); + } else if (message_reflection->HasField(message, field)) { + count = 1; + } + int data_size = 0; switch (field->type()) { #define HANDLE_TYPE(TYPE, TYPE_METHOD, CPPTYPE_METHOD) \ case FieldDescriptor::TYPE_##TYPE: \ if (field->is_repeated()) { \ for (int j = 0; j < count; j++) { \ - our_size += TYPE_METHOD##Size( \ + data_size += TYPE_METHOD##Size( \ message_reflection->GetRepeated##CPPTYPE_METHOD( \ message, field, j)); \ } \ } else { \ - our_size += TYPE_METHOD##Size( \ + data_size += TYPE_METHOD##Size( \ message_reflection->Get##CPPTYPE_METHOD(message, field)); \ } \ break; #define HANDLE_FIXED_TYPE(TYPE, TYPE_METHOD) \ case FieldDescriptor::TYPE_##TYPE: \ - our_size += count * k##TYPE_METHOD##Size; \ + data_size += count * k##TYPE_METHOD##Size; \ break; HANDLE_TYPE( INT32, Int32, Int32) @@ -793,11 +912,11 @@ int WireFormat::FieldByteSize( case FieldDescriptor::TYPE_ENUM: { if (field->is_repeated()) { for (int j = 0; j < count; j++) { - our_size += EnumSize( + data_size += EnumSize( message_reflection->GetRepeatedEnum(message, field, j)->number()); } } else { - our_size += EnumSize( + data_size += EnumSize( message_reflection->GetEnum(message, field)->number()); } break; @@ -813,13 +932,12 @@ int WireFormat::FieldByteSize( message_reflection->GetRepeatedStringReference( message, field, j, &scratch) : message_reflection->GetStringReference(message, field, &scratch); - our_size += StringSize(value); + data_size += StringSize(value); } break; } } - - return our_size; + return data_size; } int WireFormat::MessageSetItemByteSize( diff --git a/src/google/protobuf/wire_format.h b/src/google/protobuf/wire_format.h index 06f20d2a..9004caaa 100644 --- a/src/google/protobuf/wire_format.h +++ b/src/google/protobuf/wire_format.h @@ -163,13 +163,20 @@ class LIBPROTOBUF_EXPORT WireFormat { static inline WireType WireTypeForFieldType(FieldDescriptor::Type type) { return kWireTypeForFieldType[type]; } + // This is different from WireTypeForFieldType(field->type()) in the case of + // packed repeated fields. + static inline WireType WireTypeForField(const FieldDescriptor* field); // Number of bits in a tag which identify the wire type. static const int kTagTypeBits = 3; // Mask for those bits. static const uint32 kTagTypeMask = (1 << kTagTypeBits) - 1; - // Helper functions for encoding and decoding tags. (Inlined below.) + // Helper functions for encoding and decoding tags. (Inlined below and in + // _inl.h) + // + // This is different from MakeTag(field->number(), field->type()) in the case + // of packed repeated fields. static uint32 MakeTag(const FieldDescriptor* field); static uint32 MakeTag(int field_number, WireType type); static WireType GetTagWireType(uint32 tag); @@ -258,10 +265,27 @@ class LIBPROTOBUF_EXPORT WireFormat { template<typename MessageType> static inline bool ReadMessageNoVirtual(input, MessageType* value); - // Write a tag. The Write*() functions automatically include the tag, so - // normally there's no need to call this. + // Write a tag. The Write*() functions typically include the tag, so + // normally there's no need to call this unless using the Write*NoTag() + // variants. static inline bool WriteTag(field_number, WireType type, output) INL; + // Write fields, without tags. + static inline bool WriteInt32NoTag (int32 value, output) INL; + static inline bool WriteInt64NoTag (int64 value, output) INL; + static inline bool WriteUInt32NoTag (uint32 value, output) INL; + static inline bool WriteUInt64NoTag (uint64 value, output) INL; + static inline bool WriteSInt32NoTag (int32 value, output) INL; + static inline bool WriteSInt64NoTag (int64 value, output) INL; + static inline bool WriteFixed32NoTag (uint32 value, output) INL; + static inline bool WriteFixed64NoTag (uint64 value, output) INL; + static inline bool WriteSFixed32NoTag(int32 value, output) INL; + static inline bool WriteSFixed64NoTag(int64 value, output) INL; + static inline bool WriteFloatNoTag (float value, output) INL; + static inline bool WriteDoubleNoTag (double value, output) INL; + static inline bool WriteBoolNoTag (bool value, output) INL; + static inline bool WriteEnumNoTag (int value, output) INL; + // Write fields, including tags. static inline bool WriteInt32 (field_number, int32 value, output) INL; static inline bool WriteInt64 (field_number, int64 value, output) INL; @@ -355,6 +379,14 @@ class LIBPROTOBUF_EXPORT WireFormat { const FieldDescriptor* field, const Message& message); + // Computes the byte size of a field, excluding tags. For packed fields, it + // only includes the size of the raw data, and not the size of the total + // length, but for other length-delimited types, the size of the length is + // included. + static int FieldDataOnlyByteSize( + const FieldDescriptor* field, // Cannot be NULL + const Message& message); + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(WireFormat); }; @@ -368,10 +400,6 @@ class LIBPROTOBUF_EXPORT WireFormat { static_cast<uint32>( \ ((FIELD_NUMBER) << ::google::protobuf::internal::WireFormat::kTagTypeBits) | (TYPE)) -inline uint32 WireFormat::MakeTag(const FieldDescriptor* field) { - return MakeTag(field->number(), WireTypeForFieldType(field->type())); -} - inline uint32 WireFormat::MakeTag(int field_number, WireType type) { return GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(field_number, type); } diff --git a/src/google/protobuf/wire_format_inl.h b/src/google/protobuf/wire_format_inl.h index 539d8c67..451ac11c 100644 --- a/src/google/protobuf/wire_format_inl.h +++ b/src/google/protobuf/wire_format_inl.h @@ -38,6 +38,8 @@ #include <string> #include <google/protobuf/stubs/common.h> #include <google/protobuf/wire_format.h> +#include <google/protobuf/descriptor.h> +#include <google/protobuf/descriptor.pb.h> #include <google/protobuf/io/coded_stream.h> @@ -51,6 +53,19 @@ namespace google { namespace protobuf { namespace internal { +inline WireFormat::WireType WireFormat::WireTypeForField( + const FieldDescriptor* field) { + if (field->options().packed()) { + return WIRETYPE_LENGTH_DELIMITED; + } else { + return WireTypeForFieldType(field->type()); + } +} + +inline uint32 WireFormat::MakeTag(const FieldDescriptor* field) { + return MakeTag(field->number(), WireTypeForField(field)); +} + inline bool WireFormat::ReadInt32(io::CodedInputStream* input, int32* value) { uint32 temp; if (!input->ReadVarint32(&temp)) return false; @@ -210,75 +225,132 @@ inline bool WireFormat::WriteTag(int field_number, WireType type, return output->WriteTag(MakeTag(field_number, type)); } +inline bool WireFormat::WriteInt32NoTag(int32 value, + io::CodedOutputStream* output) { + return output->WriteVarint32SignExtended(value); +} +inline bool WireFormat::WriteInt64NoTag(int64 value, + io::CodedOutputStream* output) { + return output->WriteVarint64(static_cast<uint64>(value)); +} +inline bool WireFormat::WriteUInt32NoTag(uint32 value, + io::CodedOutputStream* output) { + return output->WriteVarint32(value); +} +inline bool WireFormat::WriteUInt64NoTag(uint64 value, + io::CodedOutputStream* output) { + return output->WriteVarint64(value); +} +inline bool WireFormat::WriteSInt32NoTag(int32 value, + io::CodedOutputStream* output) { + return output->WriteVarint32(ZigZagEncode32(value)); +} +inline bool WireFormat::WriteSInt64NoTag(int64 value, + io::CodedOutputStream* output) { + return output->WriteVarint64(ZigZagEncode64(value)); +} +inline bool WireFormat::WriteFixed32NoTag(uint32 value, + io::CodedOutputStream* output) { + return output->WriteLittleEndian32(value); +} +inline bool WireFormat::WriteFixed64NoTag(uint64 value, + io::CodedOutputStream* output) { + return output->WriteLittleEndian64(value); +} +inline bool WireFormat::WriteSFixed32NoTag(int32 value, + io::CodedOutputStream* output) { + return output->WriteLittleEndian32(static_cast<uint32>(value)); +} +inline bool WireFormat::WriteSFixed64NoTag(int64 value, + io::CodedOutputStream* output) { + return output->WriteLittleEndian64(static_cast<uint64>(value)); +} +inline bool WireFormat::WriteFloatNoTag(float value, + io::CodedOutputStream* output) { + return output->WriteLittleEndian32(EncodeFloat(value)); +} +inline bool WireFormat::WriteDoubleNoTag(double value, + io::CodedOutputStream* output) { + return output->WriteLittleEndian64(EncodeDouble(value)); +} +inline bool WireFormat::WriteBoolNoTag(bool value, + io::CodedOutputStream* output) { + return output->WriteVarint32(value ? 1 : 0); +} +inline bool WireFormat::WriteEnumNoTag(int value, + io::CodedOutputStream* output) { + return output->WriteVarint32SignExtended(value); +} + inline bool WireFormat::WriteInt32(int field_number, int32 value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_VARINT, output) && - output->WriteVarint32SignExtended(value); + WriteInt32NoTag(value, output); } inline bool WireFormat::WriteInt64(int field_number, int64 value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_VARINT, output) && - output->WriteVarint64(static_cast<uint64>(value)); + WriteInt64NoTag(value, output); } inline bool WireFormat::WriteUInt32(int field_number, uint32 value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_VARINT, output) && - output->WriteVarint32(value); + WriteUInt32NoTag(value, output); } inline bool WireFormat::WriteUInt64(int field_number, uint64 value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_VARINT, output) && - output->WriteVarint64(value); + WriteUInt64NoTag(value, output); } inline bool WireFormat::WriteSInt32(int field_number, int32 value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_VARINT, output) && - output->WriteVarint32(ZigZagEncode32(value)); + WriteSInt32NoTag(value, output); } inline bool WireFormat::WriteSInt64(int field_number, int64 value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_VARINT, output) && - output->WriteVarint64(ZigZagEncode64(value)); + WriteSInt64NoTag(value, output); } inline bool WireFormat::WriteFixed32(int field_number, uint32 value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_FIXED32, output) && - output->WriteLittleEndian32(value); + WriteFixed32NoTag(value, output); } inline bool WireFormat::WriteFixed64(int field_number, uint64 value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_FIXED64, output) && - output->WriteLittleEndian64(value); + WriteFixed64NoTag(value, output); } inline bool WireFormat::WriteSFixed32(int field_number, int32 value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_FIXED32, output) && - output->WriteLittleEndian32(static_cast<uint32>(value)); + WriteSFixed32NoTag(value, output); } inline bool WireFormat::WriteSFixed64(int field_number, int64 value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_FIXED64, output) && - output->WriteLittleEndian64(static_cast<uint64>(value)); + WriteSFixed64NoTag(value, output); } inline bool WireFormat::WriteFloat(int field_number, float value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_FIXED32, output) && - output->WriteLittleEndian32(EncodeFloat(value)); + WriteFloatNoTag(value, output); } inline bool WireFormat::WriteDouble(int field_number, double value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_FIXED64, output) && - output->WriteLittleEndian64(EncodeDouble(value)); + WriteDoubleNoTag(value, output); } inline bool WireFormat::WriteBool(int field_number, bool value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_VARINT, output) && - output->WriteVarint32(value ? 1 : 0); + WriteBoolNoTag(value, output); } inline bool WireFormat::WriteEnum(int field_number, int value, io::CodedOutputStream* output) { return WriteTag(field_number, WIRETYPE_VARINT, output) && - output->WriteVarint32SignExtended(value); + WriteEnumNoTag(value, output); } inline bool WireFormat::WriteString(int field_number, const string& value, diff --git a/src/google/protobuf/wire_format_unittest.cc b/src/google/protobuf/wire_format_unittest.cc index 43dccd1a..5a7c6c23 100644 --- a/src/google/protobuf/wire_format_unittest.cc +++ b/src/google/protobuf/wire_format_unittest.cc @@ -90,6 +90,40 @@ TEST(WireFormatTest, ParseExtensions) { TestUtil::ExpectAllExtensionsSet(dest); } +TEST(WireFormatTest, ParsePacked) { + unittest::TestPackedTypes source, dest; + string data; + + // Serialize using the generated code. + TestUtil::SetPackedFields(&source); + source.SerializeToString(&data); + + // Parse using WireFormat. + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + WireFormat::ParseAndMergePartial(&input, &dest); + + // Check. + TestUtil::ExpectPackedFieldsSet(dest); +} + +TEST(WireFormatTest, ParsePackedExtensions) { + unittest::TestPackedExtensions source, dest; + string data; + + // Serialize using the generated code. + TestUtil::SetPackedExtensions(&source); + source.SerializeToString(&data); + + // Parse using WireFormat. + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + WireFormat::ParseAndMergePartial(&input, &dest); + + // Check. + TestUtil::ExpectPackedExtensionsSet(dest); +} + TEST(WireFormatTest, ByteSize) { unittest::TestAllTypes message; TestUtil::SetAllFields(&message); @@ -111,6 +145,27 @@ TEST(WireFormatTest, ByteSizeExtensions) { EXPECT_EQ(0, WireFormat::ByteSize(message)); } +TEST(WireFormatTest, ByteSizePacked) { + unittest::TestPackedTypes message; + TestUtil::SetPackedFields(&message); + + EXPECT_EQ(message.ByteSize(), WireFormat::ByteSize(message)); + message.Clear(); + EXPECT_EQ(0, message.ByteSize()); + EXPECT_EQ(0, WireFormat::ByteSize(message)); +} + +TEST(WireFormatTest, ByteSizePackedExtensions) { + unittest::TestPackedExtensions message; + TestUtil::SetPackedExtensions(&message); + + EXPECT_EQ(message.ByteSize(), + WireFormat::ByteSize(message)); + message.Clear(); + EXPECT_EQ(0, message.ByteSize()); + EXPECT_EQ(0, WireFormat::ByteSize(message)); +} + TEST(WireFormatTest, Serialize) { unittest::TestAllTypes message; string generated_data; |