From 6d03f9b19bd7aa10d541f6c1a11ac71f2851eece Mon Sep 17 00:00:00 2001 From: Protobuf Team Bot Date: Mon, 14 Aug 2023 12:17:34 -0700 Subject: [PATCH] Remove option to disable the table-driven parser in protoc. It is now the default and only codegen parser implementation. Also, remove the newly dead code. PiperOrigin-RevId: 556873863 --- src/google/protobuf/compiler/cpp/file.cc | 9 +- src/google/protobuf/compiler/cpp/generator.cc | 10 - src/google/protobuf/compiler/cpp/helpers.h | 15 - src/google/protobuf/compiler/cpp/options.h | 1 - .../compiler/cpp/parse_function_generator.cc | 582 +----------------- .../compiler/cpp/parse_function_generator.h | 28 - src/google/protobuf/map_entry_lite.h | 147 ----- src/google/protobuf/map_field.h | 13 - src/google/protobuf/map_field_lite.h | 37 -- src/google/protobuf/parse_context.h | 11 - 10 files changed, 6 insertions(+), 847 deletions(-) diff --git a/src/google/protobuf/compiler/cpp/file.cc b/src/google/protobuf/compiler/cpp/file.cc index c7a4a9bdcb7ca..d0174f1215f33 100644 --- a/src/google/protobuf/compiler/cpp/file.cc +++ b/src/google/protobuf/compiler/cpp/file.cc @@ -656,8 +656,7 @@ void FileGenerator::GenerateSourceIncludes(io::Printer* p) { IncludeFile("third_party/protobuf/wire_format.h", p); } - if (HasGeneratedMethods(file_, options_) && - options_.tctable_mode != Options::kTCTableNever) { + if (HasGeneratedMethods(file_, options_)) { IncludeFile("third_party/protobuf/generated_message_tctable_impl.h", p); } @@ -703,8 +702,7 @@ void FileGenerator::GenerateSourcePrelude(io::Printer* p) { namespace _pbi = ::$proto_ns$::internal; )cc"); - if (HasGeneratedMethods(file_, options_) && - options_.tctable_mode != Options::kTCTableNever) { + if (HasGeneratedMethods(file_, options_)) { p->Emit(R"cc( namespace _fl = ::$proto_ns$::internal::field_layout; )cc"); @@ -1554,8 +1552,7 @@ void FileGenerator::GenerateLibraryIncludes(io::Printer* p) { if (HasSimpleBaseClasses(file_, options_)) { IncludeFile("third_party/protobuf/generated_message_bases.h", p); } - if (HasGeneratedMethods(file_, options_) && - options_.tctable_mode != Options::kTCTableNever) { + if (HasGeneratedMethods(file_, options_)) { IncludeFile("third_party/protobuf/generated_message_tctable_decl.h", p); } IncludeFile("third_party/protobuf/generated_message_util.h", p); diff --git a/src/google/protobuf/compiler/cpp/generator.cc b/src/google/protobuf/compiler/cpp/generator.cc index d5b39aa9842fb..442de6a9f341c 100644 --- a/src/google/protobuf/compiler/cpp/generator.cc +++ b/src/google/protobuf/compiler/cpp/generator.cc @@ -191,16 +191,6 @@ bool CppGenerator::Generate(const FileDescriptor* file, } while (pos < value.size()); } else if (key == "force_eagerly_verified_lazy") { file_options.force_eagerly_verified_lazy = true; - } else if (key == "experimental_tail_call_table_mode") { - if (value == "never") { - file_options.tctable_mode = Options::kTCTableNever; - } else if (value == "always") { - file_options.tctable_mode = Options::kTCTableAlways; - } else { - *error = absl::StrCat( - "Unknown value for experimental_tail_call_table_mode: ", value); - return false; - } } else if (key == "experimental_strip_nonfunctional_codegen") { file_options.strip_nonfunctional_codegen = true; } else { diff --git a/src/google/protobuf/compiler/cpp/helpers.h b/src/google/protobuf/compiler/cpp/helpers.h index 0ca2d55e75502..78407ed21ab93 100644 --- a/src/google/protobuf/compiler/cpp/helpers.h +++ b/src/google/protobuf/compiler/cpp/helpers.h @@ -834,10 +834,6 @@ class PROTOC_EXPORT Formatter { vars_[key] = ToString(value); } - void AddMap(const absl::flat_hash_map& vars) { - for (const auto& keyval : vars) vars_[keyval.first] = keyval.second; - } - template void operator()(const char* format, const Args&... args) const { printer_->FormatInternal({ToString(args)...}, vars_, format); @@ -868,17 +864,6 @@ class PROTOC_EXPORT Formatter { return ScopedIndenter(this); } - class PROTOC_EXPORT SaveState { - public: - explicit SaveState(Formatter* format) - : format_(format), vars_(format->vars_) {} - ~SaveState() { format_->vars_.swap(vars_); } - - private: - Formatter* format_; - absl::flat_hash_map vars_; - }; - private: io::Printer* printer_; absl::flat_hash_map vars_; diff --git a/src/google/protobuf/compiler/cpp/options.h b/src/google/protobuf/compiler/cpp/options.h index 210cc7ddbac38..3c69b59bf7863 100644 --- a/src/google/protobuf/compiler/cpp/options.h +++ b/src/google/protobuf/compiler/cpp/options.h @@ -67,7 +67,6 @@ struct Options { std::string annotation_guard_name; FieldListenerOptions field_listener_options; EnforceOptimizeMode enforce_mode = EnforceOptimizeMode::kNoEnforcement; - enum { kTCTableNever, kTCTableAlways } tctable_mode = kTCTableAlways; int num_cc_files = 0; bool safe_boundary_check = false; bool proto_h = false; diff --git a/src/google/protobuf/compiler/cpp/parse_function_generator.cc b/src/google/protobuf/compiler/cpp/parse_function_generator.cc index 2b82b154a4e0c..cc09537c51aa3 100644 --- a/src/google/protobuf/compiler/cpp/parse_function_generator.cc +++ b/src/google/protobuf/compiler/cpp/parse_function_generator.cc @@ -94,10 +94,6 @@ std::vector GetOrderedFields( return ordered_fields; } -bool HasInternalAccessors(const FieldOptions::CType ctype) { - return ctype == FieldOptions::STRING || ctype == FieldOptions::CORD; -} - } // namespace class ParseFunctionGenerator::GeneratedOptionProvider final @@ -170,10 +166,8 @@ void ParseFunctionGenerator::GenerateMethodDecls(io::Printer* printer) { void ParseFunctionGenerator::GenerateMethodImpls(io::Printer* printer) { Formatter format(printer, variables_); - bool need_parse_function = true; if (descriptor_->options().message_set_wire_format()) { // Special-case MessageSet. - need_parse_function = false; format( "const char* $classname$::_InternalParse(const char* ptr,\n" " ::_pbi::ParseContext* ctx) {\n" @@ -186,27 +180,18 @@ void ParseFunctionGenerator::GenerateMethodImpls(io::Printer* printer) { " return $extensions$.ParseMessageSet(ptr, \n" " internal_default_instance(), &_internal_metadata_, ctx);\n" "}\n"); + return; } if (HasWeakFields(descriptor_)) { // We use the reflection based one. ABSL_CHECK(HasDescriptorMethods(descriptor_->file(), options_)); - need_parse_function = false; - } - if (!should_generate_tctable()) { - if (need_parse_function) { - GenerateLoopingParseFunction(format); - } return; } - if (need_parse_function) { - GenerateTailcallParseFunction(format); - } + ABSL_CHECK(should_generate_tctable()); + GenerateTailcallParseFunction(format); } bool ParseFunctionGenerator::should_generate_tctable() const { - if (options_.tctable_mode == Options::kTCTableNever) { - return false; - } if (HasSimpleBaseClass(descriptor_, options_) || HasWeakFields(descriptor_)) { return false; } @@ -303,52 +288,6 @@ void ParseFunctionGenerator::GenerateDataDefinitions(io::Printer* printer) { GenerateTailCallTable(format); } -void ParseFunctionGenerator::GenerateLoopingParseFunction(Formatter& format) { - format( - "const char* $classname$::_InternalParse(const char* ptr, " - "::_pbi::ParseContext* ctx) {\n" - "$annotate_deserialize$" - "#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure\n"); - format.Indent(); - format.Set("msg", ""); - format.Set("this", "this"); - int hasbits_size = 0; - if (num_hasbits_ > 0) { - hasbits_size = (num_hasbits_ + 31) / 32; - } - // For now only optimize small hasbits. - if (hasbits_size != 1) hasbits_size = 0; - if (hasbits_size) { - format("_Internal::HasBits has_bits{};\n"); - format.Set("has_bits", "has_bits"); - } else { - format.Set("has_bits", "_impl_._has_bits_"); - } - format.Set("next_tag", "continue"); - format("while (!ctx->Done(&ptr)) {\n"); - format.Indent(); - - format( - "::uint32_t tag;\n" - "ptr = ::_pbi::ReadTag(ptr, &tag);\n"); - GenerateParseIterationBody(format, descriptor_, ordered_fields_); - - format.Outdent(); - format("} // while\n"); - - format.Outdent(); - format("message_done:\n"); - if (hasbits_size) format(" _impl_._has_bits_.Or(has_bits);\n"); - - format( - " return ptr;\n" - "failure:\n" - " ptr = nullptr;\n" - " goto message_done;\n" - "#undef CHK_\n" - "}\n"); -} - static NumToEntryTable MakeNumToEntryTable( const std::vector& field_descriptors) { NumToEntryTable num_to_entry_table; @@ -885,521 +824,6 @@ void ParseFunctionGenerator::GenerateFieldNames(Formatter& format) { } } -void ParseFunctionGenerator::GenerateArenaString(Formatter& format, - const FieldDescriptor* field) { - if (internal::cpp::HasHasbit(field)) { - format("_Internal::set_has_$1$(&$has_bits$);\n", FieldName(field)); - } - format( - "if (arena != nullptr) {\n" - " ptr = ctx->ReadArenaString(ptr, &$msg$$field$, arena"); - if (IsStringInlined(field, options_)) { - ABSL_DCHECK(!inlined_string_indices_.empty()); - int inlined_string_index = inlined_string_indices_[field->index()]; - ABSL_DCHECK_GT(inlined_string_index, 0); - format(", &$msg$$inlined_string_donated_array$[0], $1$, $this$", - inlined_string_index); - } else { - ABSL_DCHECK(field->default_value_string().empty()); - } - format( - ");\n" - "} else {\n" - " ptr = ::_pbi::InlineGreedyStringParser(" - "$msg$$field$.MutableNoCopy(nullptr), ptr, ctx);\n" - "}\n" - "const std::string* str = &$msg$$field$.Get(); (void)str;\n"); -} - -void ParseFunctionGenerator::GenerateStrings(Formatter& format, - const FieldDescriptor* field, - bool check_utf8) { - FieldOptions::CType ctype = FieldOptions::STRING; - - if (!field->is_repeated() && field->type() == FieldDescriptor::TYPE_BYTES && - field->options().ctype() == FieldOptions::CORD) { - ctype = FieldOptions::CORD; - } else { - if (!options_.opensource_runtime) { - // Open source doesn't support other ctypes; - ctype = field->options().ctype(); - } - } - - if (!field->is_repeated() && !options_.opensource_runtime && - GetOptimizeFor(field->file(), options_) != FileOptions::LITE_RUNTIME && - // For now only use arena string for strings with empty defaults. - field->default_value_string().empty() && - !field->real_containing_oneof() && ctype == FieldOptions::STRING) { - GenerateArenaString(format, field); - } else { - std::string parser_name; - switch (ctype) { - case FieldOptions::STRING: - parser_name = "GreedyStringParser"; - break; - case FieldOptions::CORD: - parser_name = "CordParser"; - break; - case FieldOptions::STRING_PIECE: - parser_name = "StringPieceParser"; - break; - } - // Repeated cord doesn't have _internal_mutable_$name$(), unlike other - // repeated fields. - if (field->options().ctype() == FieldOptions::CORD && - field->is_repeated() && !field->is_packable()) { - format("auto str = $msg$$field$.Add();\n"); - } else { - format("auto str = $msg$$1$mutable_$name$()$2$;\n", - HasInternalAccessors(ctype) ? "_internal_" : "", - field->is_repeated() && !field->is_packable() ? "->Add()" : ""); - } - format("ptr = ::_pbi::Inline$1$(str, ptr, ctx);\n", parser_name); - } - // It is intentionally placed before VerifyUTF8 because it doesn't make sense - // to verify UTF8 when we already know parsing failed. - format("CHK_(ptr);\n"); - if (!check_utf8) return; // return if this is a bytes field - auto level = internal::cpp::GetUtf8CheckMode( - field, - GetOptimizeFor(field->file(), options_) == FileOptions::LITE_RUNTIME); - switch (level) { - case Utf8CheckMode::kNone: - return; - case Utf8CheckMode::kVerify: - format("#ifndef NDEBUG\n"); - break; - case Utf8CheckMode::kStrict: - format("CHK_("); - break; - } - std::string field_name; - field_name = "nullptr"; - if (HasDescriptorMethods(field->file(), options_)) { - field_name = absl::StrCat("\"", field->full_name(), "\""); - } - format("::_pbi::VerifyUTF8(str, $1$)", field_name); - switch (level) { - case Utf8CheckMode::kNone: - return; - case Utf8CheckMode::kVerify: - format( - ";\n" - "#endif // !NDEBUG\n"); - break; - case Utf8CheckMode::kStrict: - format(");\n"); - break; - } -} - -void ParseFunctionGenerator::GenerateLengthDelim(Formatter& format, - const FieldDescriptor* field) { - if (field->is_packable()) { - if (field->type() == FieldDescriptor::TYPE_ENUM && - !internal::cpp::HasPreservingUnknownEnumSemantics(field)) { - std::string enum_type = QualifiedClassName(field->enum_type(), options_); - format( - "ptr = " - "::$proto_ns$::internal::Packed$1$Parser<$unknown_fields_type$>(" - "$msg$_internal_mutable_$name$(), ptr, ctx, $2$_IsValid, " - "&$msg$_internal_metadata_, $3$);\n", - DeclaredTypeMethodName(field->type()), enum_type, field->number()); - } else { - format( - "ptr = ::$proto_ns$::internal::Packed$1$Parser(" - "$msg$_internal_mutable_$name$(), ptr, ctx);\n", - DeclaredTypeMethodName(field->type())); - } - format("CHK_(ptr);\n"); - } else { - auto field_type = field->type(); - switch (field_type) { - case FieldDescriptor::TYPE_STRING: - GenerateStrings(format, field, true /* utf8 */); - break; - case FieldDescriptor::TYPE_BYTES: - GenerateStrings(format, field, false /* utf8 */); - break; - case FieldDescriptor::TYPE_MESSAGE: { - if (field->is_map()) { - const FieldDescriptor* val = field->message_type()->map_value(); - ABSL_CHECK(val); - if (val->type() == FieldDescriptor::TYPE_ENUM && - !internal::cpp::HasPreservingUnknownEnumSemantics(val)) { - format( - "auto object = " - "::$proto_ns$::internal::InitEnumParseWrapper<" - "$unknown_fields_type$>(&$msg$$field$, $1$_IsValid, " - "$2$, &$msg$_internal_metadata_);\n" - "ptr = ctx->ParseMessage(&object, ptr);\n", - QualifiedClassName(val->enum_type(), options_), - field->number()); - } else { - format("ptr = ctx->ParseMessage(&$msg$$field$, ptr);\n"); - } - } else if (IsLazy(field, options_, scc_analyzer_)) { - bool eager_verify = - IsEagerlyVerifiedLazy(field, options_, scc_analyzer_); - if (ShouldVerify(descriptor_, options_, scc_analyzer_)) { - if (eager_verify) { - format("ctx->set_lazy_eager_verify_func(&$1$::InternalVerify);\n", - ClassName(field->message_type(), true)); - } else { - format( - "ctx->set_lazy_eager_verify_func(nullptr);\n" - "auto old_mode = " - "ctx->set_lazy_parse_mode(::_pbi::ParseContext::kLazy);\n"); - } - } - if (field->real_containing_oneof()) { - format( - "if ($msg$$1$_case() != k$2$) {\n" - " $msg$clear_$1$();\n" - " $msg$$field$ = ::$proto_ns$::Arena::CreateMessage<\n" - " ::$proto_ns$::internal::LazyField>(" - "$msg$GetArenaForAllocation());\n" - " $msg$set_has_$name$();\n" - "}\n" - "auto* lazy_field = $msg$$field$;\n", - field->containing_oneof()->name(), - UnderscoresToCamelCase(field->name(), true)); - } else if (internal::cpp::HasHasbit(field)) { - format( - "_Internal::set_has_$name$(&$has_bits$);\n" - "auto* lazy_field = &$msg$$field$;\n"); - } else { - format("auto* lazy_field = &$msg$$field$;\n"); - } - format( - "::$proto_ns$::internal::LazyFieldParseHelper<\n" - " ::$proto_ns$::internal::LazyField> parse_helper(\n" - " $1$::default_instance(),\n" - " $msg$GetArenaForAllocation(),\n" - " lazy_field);\n" - "ptr = ctx->ParseMessage(&parse_helper, ptr);\n", - FieldMessageTypeName(field, options_)); - if (ShouldVerify(descriptor_, options_, scc_analyzer_)) { - if (eager_verify) { - format("ctx->set_lazy_eager_verify_func(nullptr);\n"); - } else { - format("(void)ctx->set_lazy_parse_mode(old_mode);\n"); - } - } - } else if (IsImplicitWeakField(field, options_, scc_analyzer_)) { - if (!field->is_repeated()) { - format( - "ptr = ctx->ParseMessage(_Internal::mutable_$name$($this$), " - "ptr);\n"); - } else { - format( - "ptr = ctx->ParseMessage($msg$$field$.AddWeak(" - "reinterpret_cast($1$ptr_)" - "), ptr);\n", - QualifiedDefaultInstanceName(field->message_type(), options_)); - } - } else if (IsWeak(field, options_)) { - format( - "{\n" - " auto* default_ = &reinterpret_cast($1$);\n" - " ptr = ctx->ParseMessage($msg$$weak_field_map$.MutableMessage(" - "$2$, default_), ptr);\n" - "}\n", - QualifiedDefaultInstanceName(field->message_type(), options_), - field->number()); - } else { - format( - "ptr = ctx->ParseMessage($msg$_internal_$mutable_field$(), " - "ptr);\n"); - } - format("CHK_(ptr);\n"); - break; - } - default: - ABSL_LOG(FATAL) << "Illegal combination for length delimited wiretype " - << " filed type is " << field->type(); - } - } -} - -static bool ShouldRepeat(const FieldDescriptor* descriptor, - WireFormatLite::WireType wiretype) { - constexpr int kMaxTwoByteFieldNumber = 16 * 128; - return descriptor->number() < kMaxTwoByteFieldNumber && - descriptor->is_repeated() && - (!descriptor->is_packable() || - wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED); -} - -void ParseFunctionGenerator::GenerateFieldBody( - Formatter& format, WireFormatLite::WireType wiretype, - const FieldDescriptor* field) { - Formatter::SaveState formatter_state(&format); - format.AddMap( - {{"name", FieldName(field)}, - {"primitive_type", PrimitiveTypeName(options_, field->cpp_type())}}); - if (field->is_repeated()) { - const std::string add_field = - absl::StrCat("mutable_", FieldName(field), "()->Add"); - format.AddMap({{"put_field", add_field}, {"mutable_field", add_field}}); - } else { - format.AddMap( - {{"put_field", absl::StrCat("set_", FieldName(field))}, - {"mutable_field", absl::StrCat("mutable_", FieldName(field))}}); - } - uint32_t tag = WireFormatLite::MakeTag(field->number(), wiretype); - switch (wiretype) { - case WireFormatLite::WIRETYPE_VARINT: { - if (field->type() == FieldDescriptor::TYPE_ENUM) { - format.Set("enum_type", - QualifiedClassName(field->enum_type(), options_)); - format( - "$int32$ val = ::$proto_ns$::internal::ReadVarint32(&ptr);\n" - "CHK_(ptr);\n"); - if (!internal::cpp::HasPreservingUnknownEnumSemantics(field)) { - format( - "if " - "(PROTOBUF_PREDICT_TRUE($enum_type$_IsValid(static_cast(val)" - "))) {\n"); - format.Indent(); - } - format("$msg$_internal_$put_field$(static_cast<$enum_type$>(val));\n"); - if (!internal::cpp::HasPreservingUnknownEnumSemantics(field)) { - format.Outdent(); - format( - "} else {\n" - " ::$proto_ns$::internal::WriteVarint(" - "$1$, val, $msg$mutable_unknown_fields());\n" - "}\n", - field->number()); - } - } else { - std::string size = (field->type() == FieldDescriptor::TYPE_INT32 || - field->type() == FieldDescriptor::TYPE_SINT32 || - field->type() == FieldDescriptor::TYPE_UINT32) - ? "32" - : "64"; - std::string zigzag; - if ((field->type() == FieldDescriptor::TYPE_SINT32 || - field->type() == FieldDescriptor::TYPE_SINT64)) { - zigzag = "ZigZag"; - } - if (field->is_repeated() || field->real_containing_oneof()) { - format( - "$msg$_internal_$put_field$(" - "::$proto_ns$::internal::ReadVarint$1$$2$(&ptr));\n" - "CHK_(ptr);\n", - zigzag, size); - } else { - if (internal::cpp::HasHasbit(field)) { - format("_Internal::set_has_$name$(&$has_bits$);\n"); - } - format( - "$msg$$field$ = ::$proto_ns$::internal::ReadVarint$1$$2$(&ptr);\n" - "CHK_(ptr);\n", - zigzag, size); - } - } - break; - } - case WireFormatLite::WIRETYPE_FIXED32: - case WireFormatLite::WIRETYPE_FIXED64: { - if (field->is_repeated() || field->real_containing_oneof()) { - format( - "$msg$_internal_$put_field$(" - "::$proto_ns$::internal::UnalignedLoad<$primitive_type$>(ptr));\n" - "ptr += sizeof($primitive_type$);\n"); - } else { - if (internal::cpp::HasHasbit(field)) { - format("_Internal::set_has_$name$(&$has_bits$);\n"); - } - format( - "$msg$$field$ = " - "::$proto_ns$::internal::UnalignedLoad<$primitive_type$>(ptr);\n" - "ptr += sizeof($primitive_type$);\n"); - } - break; - } - case WireFormatLite::WIRETYPE_LENGTH_DELIMITED: { - GenerateLengthDelim(format, field); - break; - } - case WireFormatLite::WIRETYPE_START_GROUP: { - format( - "ptr = ctx->ParseGroup($msg$_internal_$mutable_field$(), ptr, $1$);\n" - "CHK_(ptr);\n", - tag); - break; - } - case WireFormatLite::WIRETYPE_END_GROUP: { - ABSL_LOG(FATAL) << "Can't have end group field\n"; - break; - } - } // switch (wire_type) -} - -// Returns the tag for this field and in case of repeated packable fields, -// sets a fallback tag in fallback_tag_ptr. -static uint32_t ExpectedTag(const FieldDescriptor* field, - uint32_t* fallback_tag_ptr) { - uint32_t expected_tag; - if (field->is_packable()) { - auto expected_wiretype = WireFormat::WireTypeForFieldType(field->type()); - expected_tag = WireFormatLite::MakeTag(field->number(), expected_wiretype); - ABSL_CHECK(expected_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED); - auto fallback_wiretype = WireFormatLite::WIRETYPE_LENGTH_DELIMITED; - uint32_t fallback_tag = - WireFormatLite::MakeTag(field->number(), fallback_wiretype); - - if (field->is_packed()) std::swap(expected_tag, fallback_tag); - *fallback_tag_ptr = fallback_tag; - } else { - auto expected_wiretype = WireFormat::WireTypeForField(field); - expected_tag = WireFormatLite::MakeTag(field->number(), expected_wiretype); - } - return expected_tag; -} - -// These variables are used by the generated parse iteration, and must already -// be defined in the generated code: -// - `const char* ptr`: the input buffer. -// - `ParseContext* ctx`: the associated context for `ptr`. -// - implicit `this`: i.e., we must be in a non-static member function. -// -// The macro `CHK_(x)` must be defined. It should return an error condition if -// the macro parameter is false. -// -// Whenever an END_GROUP tag was read, or tag 0 was read, the generated code -// branches to the label `message_done`. -// -// These formatter variables are used: -// - `next_tag`: a single statement to begin parsing the next tag. -// -// At the end of the generated code, the enclosing function should proceed to -// parse the next tag in the stream. -void ParseFunctionGenerator::GenerateParseIterationBody( - Formatter& format, const Descriptor* descriptor, - const std::vector& fields) { - if (!fields.empty()) { - GenerateFieldSwitch(format, fields); - // Each field `case` only considers field number. Field numbers that are - // not defined in the message, or tags with an incompatible wire type, are - // considered "unusual" cases. They will be handled by the logic below. - format.Outdent(); - format("handle_unusual:\n"); - format.Indent(); - } - - // Unusual/extension/unknown case: - format( - "if ((tag == 0) || ((tag & 7) == 4)) {\n" - " CHK_(ptr);\n" - " ctx->SetLastTag(tag);\n" - " goto message_done;\n" - "}\n"); - if (IsMapEntryMessage(descriptor)) { - format("$next_tag$;\n"); - } else { - if (descriptor->extension_range_count() > 0) { - format("if ("); - for (int i = 0; i < descriptor->extension_range_count(); i++) { - const Descriptor::ExtensionRange* range = - descriptor->extension_range(i); - if (i > 0) format(" ||\n "); - - uint32_t start_tag = WireFormatLite::MakeTag( - range->start_number(), static_cast(0)); - uint32_t end_tag = WireFormatLite::MakeTag( - range->end_number(), static_cast(0)); - - if (range->end_number() > FieldDescriptor::kMaxNumber) { - format("($1$u <= tag)", start_tag); - } else { - format("($1$u <= tag && tag < $2$u)", start_tag, end_tag); - } - } - format( - ") {\n" - " ptr = $msg$$extensions$.ParseField(tag, ptr, " - "internal_default_instance(), &$msg$_internal_metadata_, ctx);\n" - " CHK_(ptr != nullptr);\n" - " $next_tag$;\n" - "}\n"); - } - format( - "ptr = UnknownFieldParse(\n" - " tag,\n" - " $msg$_internal_metadata_.mutable_unknown_fields<" - "$unknown_fields_type$>(),\n" - " ptr, ctx);\n" - "CHK_(ptr != nullptr);\n"); - } -} - -void ParseFunctionGenerator::GenerateFieldSwitch( - Formatter& format, const std::vector& fields) { - format("switch (tag >> 3) {\n"); - format.Indent(); - - for (const auto* field : fields) { - bool cold = ShouldSplit(field, options_); - format.Set("field", FieldMemberName(field, cold)); - PrintFieldComment(format, field, options_); - format("case $1$:\n", field->number()); - format.Indent(); - uint32_t fallback_tag = 0; - uint32_t expected_tag = ExpectedTag(field, &fallback_tag); - format("if (PROTOBUF_PREDICT_TRUE(static_cast<$uint8$>(tag) == $1$)) {\n", - expected_tag & 0xFF); - format.Indent(); - if (cold) { - format("$msg$PrepareSplitMessageForWrite();\n"); - } - auto wiretype = WireFormatLite::GetTagWireType(expected_tag); - uint32_t tag = WireFormatLite::MakeTag(field->number(), wiretype); - int tag_size = io::CodedOutputStream::VarintSize32(tag); - bool is_repeat = ShouldRepeat(field, wiretype); - if (is_repeat) { - format( - "ptr -= $1$;\n" - "do {\n" - " ptr += $1$;\n", - tag_size); - format.Indent(); - } - GenerateFieldBody(format, wiretype, field); - if (is_repeat) { - format.Outdent(); - format( - " if (!ctx->DataAvailable(ptr)) break;\n" - "} while (::$proto_ns$::internal::ExpectTag<$1$>(ptr));\n", - tag); - } - format.Outdent(); - if (fallback_tag) { - format("} else if (static_cast<$uint8$>(tag) == $1$) {\n", - fallback_tag & 0xFF); - format.Indent(); - GenerateFieldBody(format, WireFormatLite::GetTagWireType(fallback_tag), - field); - format.Outdent(); - } - format( - "} else {\n" - " goto handle_unusual;\n" - "}\n" - "$next_tag$;\n"); - format.Outdent(); - } // for loop over ordered fields - - format( - "default:\n" - " goto handle_unusual;\n"); - format.Outdent(); - format("} // switch\n"); -} - } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/parse_function_generator.h b/src/google/protobuf/compiler/cpp/parse_function_generator.h index 498fab462a942..405d00ba3386b 100644 --- a/src/google/protobuf/compiler/cpp/parse_function_generator.h +++ b/src/google/protobuf/compiler/cpp/parse_function_generator.h @@ -79,40 +79,12 @@ class ParseFunctionGenerator { // Generates a tail-calling `_InternalParse` function. void GenerateTailcallParseFunction(Formatter& format); - // Generates a looping `_InternalParse` function. - void GenerateLoopingParseFunction(Formatter& format); - // Generates the tail-call table definition. void GenerateTailCallTable(Formatter& format); void GenerateFastFieldEntries(Formatter& format); void GenerateFieldEntries(Formatter& format); void GenerateFieldNames(Formatter& format); - // Generates parsing code for an `ArenaString` field. - void GenerateArenaString(Formatter& format, const FieldDescriptor* field); - - // Generates parsing code for a string-typed field. - void GenerateStrings(Formatter& format, const FieldDescriptor* field, - bool check_utf8); - - // Generates parsing code for a length-delimited field (strings, messages, - // etc.). - void GenerateLengthDelim(Formatter& format, const FieldDescriptor* field); - - // Generates the parsing code for a known field. - void GenerateFieldBody(Formatter& format, - google::protobuf::internal::WireFormatLite::WireType wiretype, - const FieldDescriptor* field); - - // Generates code to parse the next field from the input stream. - void GenerateParseIterationBody( - Formatter& format, const Descriptor* descriptor, - const std::vector& fields); - - // Generates a `switch` statement to parse each of `fields`. - void GenerateFieldSwitch(Formatter& format, - const std::vector& fields); - const Descriptor* descriptor_; MessageSCCAnalyzer* scc_analyzer_; const Options& options_; diff --git a/src/google/protobuf/map_entry_lite.h b/src/google/protobuf/map_entry_lite.h index bebaf67648713..f20182453321f 100644 --- a/src/google/protobuf/map_entry_lite.h +++ b/src/google/protobuf/map_entry_lite.h @@ -74,34 +74,6 @@ namespace google { namespace protobuf { namespace internal { -// MoveHelper::Move is used to set *dest. It copies *src, or moves it (in -// the C++11 sense), or swaps it. *src is left in a sane state for -// subsequent destruction, but shouldn't be used for anything. -template -struct MoveHelper { // primitives - static void Move(T* src, T* dest) { *dest = *src; } -}; - -template -struct MoveHelper { // enums - static void Move(T* src, T* dest) { *dest = *src; } - // T is an enum here, so allow conversions to and from int. - static void Move(T* src, int* dest) { *dest = static_cast(*src); } - static void Move(int* src, T* dest) { *dest = static_cast(*src); } -}; - -template -struct MoveHelper { // messages - static void Move(T* src, T* dest) { dest->Swap(src); } -}; - -template -struct MoveHelper { // strings and similar - static void Move(T* src, T* dest) { - *dest = std::move(*src); - } -}; - // MapEntryImpl is used to implement parsing and serialization of map entries. // It uses Curiously Recurring Template Pattern (CRTP) to provide the type of // the eventual code to the template code. @@ -288,125 +260,6 @@ class MapEntryImpl : public Base { clear_has_value(); } - // Parsing using MergePartialFromCodedStream, above, is not as - // efficient as it could be. This helper class provides a speedier way. - template - class Parser { - public: - explicit Parser(MapField* mf) : mf_(mf), map_(mf->MutableMap()) {} - ~Parser() { - if (entry_ != nullptr && entry_->GetArenaForAllocation() == nullptr) - delete entry_; - } - - const char* _InternalParse(const char* ptr, ParseContext* ctx) { - if (PROTOBUF_PREDICT_TRUE(!ctx->Done(&ptr) && *ptr == kKeyTag)) { - ptr = KeyTypeHandler::Read(ptr + 1, ctx, &key_); - if (PROTOBUF_PREDICT_FALSE(!ptr || !Derived::ValidateKey(&key_))) { - return nullptr; - } - if (PROTOBUF_PREDICT_TRUE(!ctx->Done(&ptr) && *ptr == kValueTag)) { - typename Map::size_type map_size = map_->size(); - value_ptr_ = &(*map_)[key_]; - if (PROTOBUF_PREDICT_TRUE(map_size != map_->size())) { - using T = - typename MapIf::type; - ptr = ValueTypeHandler::Read(ptr + 1, ctx, - reinterpret_cast(value_ptr_)); - if (PROTOBUF_PREDICT_FALSE(!ptr || - !Derived::ValidateValue(value_ptr_))) { - map_->erase(key_); // Failure! Undo insertion. - return nullptr; - } - if (PROTOBUF_PREDICT_TRUE(ctx->Done(&ptr))) return ptr; - if (!ptr) return nullptr; - NewEntry(); - ValueMover::Move(value_ptr_, entry_->mutable_value()); - map_->erase(key_); - goto move_key; - } - } else { - if (!ptr) return nullptr; - } - NewEntry(); - move_key: - KeyMover::Move(&key_, entry_->mutable_key()); - } else { - if (!ptr) return nullptr; - NewEntry(); - } - ptr = entry_->_InternalParse(ptr, ctx); - if (ptr) UseKeyAndValueFromEntry(); - return ptr; - } - - template - const char* ParseWithEnumValidation(const char* ptr, ParseContext* ctx, - bool (*is_valid)(int), - uint32_t field_num, - InternalMetadata* metadata) { - auto entry = NewEntry(); - ptr = entry->_InternalParse(ptr, ctx); - if (!ptr) return nullptr; - if (is_valid(entry->value())) { - UseKeyAndValueFromEntry(); - } else { - WriteLengthDelimited(field_num, entry->SerializeAsString(), - metadata->mutable_unknown_fields()); - } - return ptr; - } - - MapEntryImpl* NewEntry() { return entry_ = mf_->NewEntry(); } - - const Key& key() const { return key_; } - const Value& value() const { return *value_ptr_; } - - const Key& entry_key() const { return entry_->key(); } - const Value& entry_value() const { return entry_->value(); } - - private: - void UseKeyAndValueFromEntry() { - // Update key_ in case we need it later (because key() is called). - // This is potentially inefficient, especially if the key is - // expensive to copy (e.g., a long string), but this is a cold - // path, so it's not a big deal. - key_ = entry_->key(); - value_ptr_ = &(*map_)[key_]; - ValueMover::Move(entry_->mutable_value(), value_ptr_); - } - - // After reading a key and value successfully, and inserting that data - // into map_, we are not at the end of the input. This is unusual, but - // allowed by the spec. - bool ReadBeyondKeyValuePair(io::CodedInputStream* input) PROTOBUF_COLD { - NewEntry(); - ValueMover::Move(value_ptr_, entry_->mutable_value()); - map_->erase(key_); - KeyMover::Move(&key_, entry_->mutable_key()); - const bool result = entry_->MergePartialFromCodedStream(input); - if (result) UseKeyAndValueFromEntry(); - return result; - } - - typedef MoveHelper - KeyMover; - typedef MoveHelper - ValueMover; - - MapField* const mf_; - Map* const map_; - Key key_; - Value* value_ptr_; - MapEntryImpl* entry_ = nullptr; - }; - protected: void set_has_key() { _has_bits_[0] |= 0x00000001u; } bool has_key() const { return (_has_bits_[0] & 0x00000001u) != 0; } diff --git a/src/google/protobuf/map_field.h b/src/google/protobuf/map_field.h index 308e9ac0749ab..c8e755b4aebd8 100644 --- a/src/google/protobuf/map_field.h +++ b/src/google/protobuf/map_field.h @@ -624,19 +624,6 @@ class MapField final : public TypeDefinedMapFieldBase { return Arena::CreateMessage(this->arena()); } - const char* _InternalParse(const char* ptr, ParseContext* ctx) { - typename Derived::template Parser> parser(this); - return parser._InternalParse(ptr, ctx); - } - template - const char* ParseWithEnumValidation(const char* ptr, ParseContext* ctx, - bool (*is_valid)(int), uint32_t field_num, - InternalMetadata* metadata) { - typename Derived::template Parser> parser(this); - return parser.template ParseWithEnumValidation( - ptr, ctx, is_valid, field_num, metadata); - } - private: typedef void InternalArenaConstructable_; typedef void DestructorSkippable_; diff --git a/src/google/protobuf/map_field_lite.h b/src/google/protobuf/map_field_lite.h index 17612846b95a6..e8fb309f46f5c 100644 --- a/src/google/protobuf/map_field_lite.h +++ b/src/google/protobuf/map_field_lite.h @@ -104,20 +104,6 @@ class MapFieldLite { return Arena::CreateMessage(map_.arena()); } - const char* _InternalParse(const char* ptr, ParseContext* ctx) { - typename Derived::template Parser> parser(this); - return parser._InternalParse(ptr, ctx); - } - - template - const char* ParseWithEnumValidation(const char* ptr, ParseContext* ctx, - bool (*is_valid)(int), uint32_t field_num, - InternalMetadata* metadata) { - typename Derived::template Parser> parser(this); - return parser.template ParseWithEnumValidation( - ptr, ctx, is_valid, field_num, metadata); - } - private: typedef void DestructorSkippable_; @@ -130,29 +116,6 @@ class MapFieldLite { friend class google::protobuf::Arena; }; -template -struct EnumParseWrapper { - const char* _InternalParse(const char* ptr, ParseContext* ctx) { - return map_field->template ParseWithEnumValidation( - ptr, ctx, is_valid, field_num, metadata); - } - T* map_field; - bool (*is_valid)(int); - uint32_t field_num; - InternalMetadata* metadata; -}; - -// Helper function because the typenames of maps are horrendous to print. This -// leverages compiler type deduction, to keep all type data out of the -// generated code -template -EnumParseWrapper InitEnumParseWrapper( - T* map_field, bool (*is_valid)(int), uint32_t field_num, - InternalMetadata* metadata) { - return EnumParseWrapper{map_field, is_valid, field_num, - metadata}; -} - // True if IsInitialized() is true for value field in all elements of t. T is // expected to be message. It's useful to have this helper here to keep the // protobuf compiler from ever having to emit loops in IsInitialized() methods. diff --git a/src/google/protobuf/parse_context.h b/src/google/protobuf/parse_context.h index 1b5e8771b5f7b..3506d38915acc 100644 --- a/src/google/protobuf/parse_context.h +++ b/src/google/protobuf/parse_context.h @@ -611,17 +611,6 @@ class PROTOBUF_EXPORT ParseContext : public EpsCopyInputStream { Data data_; }; -template -bool ExpectTag(const char* ptr) { - if (tag < 128) { - return *ptr == static_cast(tag); - } else { - static_assert(tag < 128 * 128, "We only expect tags for 1 or 2 bytes"); - char buf[2] = {static_cast(tag | 0x80), static_cast(tag >> 7)}; - return std::memcmp(ptr, buf, 2) == 0; - } -} - template struct EndianHelper;