blob: a0575708580d17ed813391dd1293e4768b3bc39d [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "google/protobuf/compiler/cpp/parse_function_generator.h"
#include <algorithm>
#include <limits>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/strings/str_cat.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/generated_message_tctable_gen.h"
#include "google/protobuf/generated_message_tctable_impl.h"
#include "google/protobuf/wire_format.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
using internal::TailCallTableInfo;
using internal::cpp::Utf8CheckMode;
using google::protobuf::internal::WireFormat;
using google::protobuf::internal::WireFormatLite;
bool UseDirectTcParserTable(const FieldDescriptor* field,
const Options& options) {
if (field->cpp_type() != field->CPPTYPE_MESSAGE) return false;
auto* m = field->message_type();
return !m->options().message_set_wire_format() &&
m->file()->options().optimize_for() != FileOptions::CODE_SIZE &&
!HasSimpleBaseClass(m, options) && !HasTracker(m, options)
; // NOLINT(whitespace/semicolon)
}
std::vector<const FieldDescriptor*> GetOrderedFields(
const Descriptor* descriptor, const Options& options) {
std::vector<const FieldDescriptor*> ordered_fields;
for (auto field : FieldRange(descriptor)) {
if (!IsFieldStripped(field, options)) {
ordered_fields.push_back(field);
}
}
std::sort(ordered_fields.begin(), ordered_fields.end(),
[](const FieldDescriptor* a, const FieldDescriptor* b) {
return a->number() < b->number();
});
return ordered_fields;
}
bool HasInternalAccessors(const FieldOptions::CType ctype) {
return ctype == FieldOptions::STRING || ctype == FieldOptions::CORD;
}
} // namespace
class ParseFunctionGenerator::GeneratedOptionProvider final
: public internal::TailCallTableInfo::OptionProvider {
public:
explicit GeneratedOptionProvider(ParseFunctionGenerator* gen) : gen_(gen) {}
TailCallTableInfo::PerFieldOptions GetForField(
const FieldDescriptor* field) const final {
const auto verify_flag = [&] {
if (IsEagerlyVerifiedLazy(field, gen_->options_, gen_->scc_analyzer_))
return internal::field_layout::kTvEager;
if (IsLazilyVerifiedLazy(field, gen_->options_))
return internal::field_layout::kTvLazy;
return internal::field_layout::TransformValidation{};
};
return {verify_flag(),
IsStringInlined(field, gen_->options_),
IsImplicitWeakField(field, gen_->options_, gen_->scc_analyzer_),
UseDirectTcParserTable(field, gen_->options_),
GetOptimizeFor(field->file(), gen_->options_) ==
FileOptions::LITE_RUNTIME,
ShouldSplit(field, gen_->options_)};
}
private:
ParseFunctionGenerator* gen_;
};
ParseFunctionGenerator::ParseFunctionGenerator(
const Descriptor* descriptor, int max_has_bit_index,
const std::vector<int>& has_bit_indices,
const std::vector<int>& inlined_string_indices, const Options& options,
MessageSCCAnalyzer* scc_analyzer,
const absl::flat_hash_map<absl::string_view, std::string>& vars)
: descriptor_(descriptor),
scc_analyzer_(scc_analyzer),
options_(options),
variables_(vars),
inlined_string_indices_(inlined_string_indices),
ordered_fields_(GetOrderedFields(descriptor_, options_)),
num_hasbits_(max_has_bit_index) {
if (should_generate_tctable()) {
tc_table_info_.reset(new TailCallTableInfo(
descriptor_, ordered_fields_, GeneratedOptionProvider(this),
has_bit_indices, inlined_string_indices));
}
SetCommonMessageDataVariables(descriptor_, &variables_);
SetUnknownFieldsVariable(descriptor_, options_, &variables_);
variables_["classname"] = ClassName(descriptor, false);
}
void ParseFunctionGenerator::GenerateMethodDecls(io::Printer* printer) {
Formatter format(printer, variables_);
if (should_generate_tctable()) {
format.Outdent();
if (should_generate_guarded_tctable()) {
format("#ifdef PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n");
}
format(
" private:\n"
" static const char* Tct_ParseFallback(PROTOBUF_TC_PARAM_DECL);\n"
" public:\n");
if (should_generate_guarded_tctable()) {
format("#endif\n");
}
format.Indent();
}
format(
"const char* _InternalParse(const char* ptr, "
"::$proto_ns$::internal::ParseContext* ctx) final;\n");
}
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"
"$annotate_deserialize$");
if (!options_.unverified_lazy_message_sets &&
ShouldVerify(descriptor_, options_, scc_analyzer_)) {
format(
" ctx->set_lazy_eager_verify_func(&$classname$::InternalVerify);\n");
}
format(
" return $extensions$.ParseMessageSet(ptr, \n"
" internal_default_instance(), &_internal_metadata_, ctx);\n"
"}\n");
}
if (!should_generate_tctable()) {
if (need_parse_function) {
GenerateLoopingParseFunction(format);
}
return;
}
if (should_generate_guarded_tctable()) {
format("#ifdef PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n\n");
}
if (need_parse_function) {
GenerateTailcallParseFunction(format);
}
if (tc_table_info_->use_generated_fallback) {
GenerateTailcallFallbackFunction(format);
}
if (should_generate_guarded_tctable()) {
if (need_parse_function) {
format("\n#else // PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n\n");
GenerateLoopingParseFunction(format);
}
format("\n#endif // PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n");
}
}
bool ParseFunctionGenerator::should_generate_tctable() const {
if (options_.tctable_mode == Options::kTCTableNever) {
return false;
}
if (HasSimpleBaseClass(descriptor_, options_)) {
return false;
}
return true;
}
void ParseFunctionGenerator::GenerateTailcallParseFunction(Formatter& format) {
GOOGLE_CHECK(should_generate_tctable());
// Generate an `_InternalParse` that starts the tail-calling loop.
format(
"const char* $classname$::_InternalParse(\n"
" const char* ptr, ::_pbi::ParseContext* ctx) {\n"
"$annotate_deserialize$"
" ptr = ::_pbi::TcParser::ParseLoop(this, ptr, ctx, "
"&_table_.header);\n");
format(
" return ptr;\n"
"}\n\n");
}
static bool NeedsUnknownEnumSupport(const Descriptor* descriptor) {
for (int i = 0; i < descriptor->field_count(); ++i) {
auto* field = descriptor->field(i);
if (field->is_repeated() && field->cpp_type() == field->CPPTYPE_ENUM &&
!internal::cpp::HasPreservingUnknownEnumSemantics(field)) {
return true;
}
}
return false;
}
void ParseFunctionGenerator::GenerateTailcallFallbackFunction(
Formatter& format) {
GOOGLE_CHECK(should_generate_tctable());
format(
"const char* $classname$::Tct_ParseFallback(PROTOBUF_TC_PARAM_DECL) {\n"
"#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) return nullptr\n");
format.Indent();
format("auto* typed_msg = static_cast<$classname$*>(msg);\n");
// If we need a side channel, generate the check to jump to the generic
// handler to deal with the side channel data.
if (NeedsUnknownEnumSupport(descriptor_)) {
format(
"if (PROTOBUF_PREDICT_FALSE(\n"
" _pbi::TcParser::MustFallbackToGeneric(PROTOBUF_TC_PARAM_PASS))) "
"{\n"
" PROTOBUF_MUSTTAIL return "
"::_pbi::TcParser::GenericFallback$1$(PROTOBUF_TC_PARAM_PASS);\n"
"}\n",
GetOptimizeFor(descriptor_->file(), options_) ==
FileOptions::LITE_RUNTIME
? "Lite"
: "");
}
if (num_hasbits_ > 0) {
// Sync hasbits
format("typed_msg->_impl_._has_bits_[0] |= hasbits;\n");
}
format("::uint32_t tag = data.tag();\n");
format.Set("msg", "typed_msg->");
format.Set("this", "typed_msg");
format.Set("has_bits", "typed_msg->_impl_._has_bits_");
format.Set("next_tag", "goto next_tag");
GenerateParseIterationBody(format, descriptor_,
tc_table_info_->fallback_fields);
format.Outdent();
format(
"next_tag:\n"
"message_done:\n"
" return ptr;\n"
"#undef CHK_\n"
"}\n");
}
struct SkipEntry16 {
uint16_t skipmap;
uint16_t field_entry_offset;
};
struct SkipEntryBlock {
uint32_t first_fnum;
std::vector<SkipEntry16> entries;
};
struct NumToEntryTable {
uint32_t skipmap32; // for fields #1 - #32
std::vector<SkipEntryBlock> blocks;
// Compute the number of uint16_t required to represent this table.
int size16() const {
int size = 2; // for the termination field#
for (const auto& block : blocks) {
// 2 for the field#, 1 for a count of skip entries, 2 for each entry.
size += 3 + block.entries.size() * 2;
}
return size;
}
};
static NumToEntryTable MakeNumToEntryTable(
const std::vector<const FieldDescriptor*>& field_descriptors);
static int FieldNameDataSize(const std::vector<uint8_t>& data) {
// We add a +1 here to allow for a NUL termination character. It makes the
// codegen nicer.
return data.empty() ? 0 : data.size() + 1;
}
void ParseFunctionGenerator::GenerateDataDecls(io::Printer* printer) {
if (!should_generate_tctable()) {
return;
}
Formatter format(printer, variables_);
if (should_generate_guarded_tctable()) {
format.Outdent();
format("#ifdef PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n");
format.Indent();
}
auto field_num_to_entry_table = MakeNumToEntryTable(ordered_fields_);
format(
"friend class ::$proto_ns$::internal::TcParser;\n"
"static const ::$proto_ns$::internal::"
"TcParseTable<$1$, $2$, $3$, $4$, $5$> _table_;\n",
tc_table_info_->table_size_log2, ordered_fields_.size(),
tc_table_info_->aux_entries.size(),
FieldNameDataSize(tc_table_info_->field_name_data),
field_num_to_entry_table.size16());
if (should_generate_guarded_tctable()) {
format.Outdent();
format("#endif // PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n");
format.Indent();
}
}
void ParseFunctionGenerator::GenerateDataDefinitions(io::Printer* printer) {
if (!should_generate_tctable()) {
return;
}
Formatter format(printer, variables_);
if (should_generate_guarded_tctable()) {
format("#ifdef PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n");
}
GenerateTailCallTable(format);
if (should_generate_guarded_tctable()) {
format("#endif // PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n");
}
}
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<const FieldDescriptor*>& field_descriptors) {
NumToEntryTable num_to_entry_table;
num_to_entry_table.skipmap32 = static_cast<uint32_t>(-1);
// skip_entry_block is the current block of SkipEntries that we're
// appending to. cur_block_first_fnum is the number of the first
// field represented by the block.
uint16_t field_entry_index = 0;
uint16_t N = field_descriptors.size();
// First, handle field numbers 1-32, which affect only the initial
// skipmap32 and don't generate additional skip-entry blocks.
for (; field_entry_index != N; ++field_entry_index) {
auto* field_descriptor = field_descriptors[field_entry_index];
if (field_descriptor->number() > 32) break;
auto skipmap32_index = field_descriptor->number() - 1;
num_to_entry_table.skipmap32 -= 1 << skipmap32_index;
}
// If all the field numbers were less than or equal to 32, we will have
// no further entries to process, and we are already done.
if (field_entry_index == N) return num_to_entry_table;
SkipEntryBlock* block = nullptr;
bool start_new_block = true;
// To determine sparseness, track the field number corresponding to
// the start of the most recent skip entry.
uint32_t last_skip_entry_start = 0;
for (; field_entry_index != N; ++field_entry_index) {
auto* field_descriptor = field_descriptors[field_entry_index];
uint32_t fnum = field_descriptor->number();
GOOGLE_CHECK_GT(fnum, last_skip_entry_start);
if (start_new_block == false) {
// If the next field number is within 15 of the last_skip_entry_start, we
// continue writing just to that entry. If it's between 16 and 31 more,
// then we just extend the current block by one. If it's more than 31
// more, we have to add empty skip entries in order to continue using the
// existing block. Obviously it's just 32 more, it doesn't make sense to
// start a whole new block, since new blocks mean having to write out
// their starting field number, which is 32 bits, as well as the size of
// the additional block, which is 16... while an empty SkipEntry16 only
// costs 32 bits. So if it was 48 more, it's a slight space win; we save
// 16 bits, but probably at the cost of slower run time. We're choosing
// 96 for now.
if (fnum - last_skip_entry_start > 96) start_new_block = true;
}
if (start_new_block) {
num_to_entry_table.blocks.push_back(SkipEntryBlock{fnum});
block = &num_to_entry_table.blocks.back();
start_new_block = false;
}
auto skip_entry_num = (fnum - block->first_fnum) / 16;
auto skip_entry_index = (fnum - block->first_fnum) % 16;
while (skip_entry_num >= block->entries.size())
block->entries.push_back({0xFFFF, field_entry_index});
block->entries[skip_entry_num].skipmap -= 1 << (skip_entry_index);
last_skip_entry_start = fnum - skip_entry_index;
}
return num_to_entry_table;
}
void ParseFunctionGenerator::GenerateTailCallTable(Formatter& format) {
GOOGLE_CHECK(should_generate_tctable());
// All entries without a fast-path parsing function need a fallback.
std::string fallback;
if (tc_table_info_->use_generated_fallback) {
fallback = ClassName(descriptor_) + "::Tct_ParseFallback";
} else {
fallback = "::_pbi::TcParser::GenericFallback";
if (GetOptimizeFor(descriptor_->file(), options_) ==
FileOptions::LITE_RUNTIME) {
fallback += "Lite";
}
}
// For simplicity and speed, the table is not covering all proto
// configurations. This model uses a fallback to cover all situations that
// the table can't accommodate, together with unknown fields or extensions.
// These are number of fields over 32, fields with 3 or more tag bytes,
// maps, weak fields, lazy, more than 1 extension range. In the cases
// the table is sufficient we can use a generic routine, that just handles
// unknown fields and potentially an extension range.
auto field_num_to_entry_table = MakeNumToEntryTable(ordered_fields_);
format(
"PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1\n"
"const ::_pbi::TcParseTable<$1$, $2$, $3$, $4$, $5$> "
"$classname$::_table_ = "
"{\n",
tc_table_info_->table_size_log2, ordered_fields_.size(),
tc_table_info_->aux_entries.size(),
FieldNameDataSize(tc_table_info_->field_name_data),
field_num_to_entry_table.size16());
{
auto table_scope = format.ScopedIndent();
format("{\n");
{
auto header_scope = format.ScopedIndent();
if (num_hasbits_ > 0 || IsMapEntryMessage(descriptor_)) {
format("PROTOBUF_FIELD_OFFSET($classname$, _impl_._has_bits_),\n");
} else {
format("0, // no _has_bits_\n");
}
if (descriptor_->extension_range_count() == 1) {
format(
"PROTOBUF_FIELD_OFFSET($classname$, $extensions$),\n"
"$1$, $2$, // extension_range_{low,high}\n",
descriptor_->extension_range(0)->start,
descriptor_->extension_range(0)->end);
} else {
format("0, 0, 0, // no _extensions_\n");
}
format("$1$, $2$, // max_field_number, fast_idx_mask\n",
(ordered_fields_.empty() ? 0 : ordered_fields_.back()->number()),
(((1 << tc_table_info_->table_size_log2) - 1) << 3));
format(
"offsetof(decltype(_table_), field_lookup_table),\n"
"$1$, // skipmap\n",
field_num_to_entry_table.skipmap32);
if (ordered_fields_.empty()) {
format(
"offsetof(decltype(_table_), field_names), // no field_entries\n");
} else {
format("offsetof(decltype(_table_), field_entries),\n");
}
format(
"$1$, // num_field_entries\n"
"$2$, // num_aux_entries\n",
ordered_fields_.size(), tc_table_info_->aux_entries.size());
if (tc_table_info_->aux_entries.empty()) {
format(
"offsetof(decltype(_table_), field_names), // no aux_entries\n");
} else {
format("offsetof(decltype(_table_), aux_entries),\n");
}
format(
"&$1$._instance,\n"
"$2$, // fallback\n"
"",
DefaultInstanceName(descriptor_, options_), fallback);
}
format("}, {{\n");
{
// fast_entries[]
auto fast_scope = format.ScopedIndent();
GenerateFastFieldEntries(format);
}
format("}}, {{\n");
{
// field_lookup_table[]
auto field_lookup_scope = format.ScopedIndent();
int line_entries = 0;
for (int i = 0, N = field_num_to_entry_table.blocks.size(); i < N; ++i) {
SkipEntryBlock& entry_block = field_num_to_entry_table.blocks[i];
format("$1$, $2$, $3$,\n", entry_block.first_fnum & 65535,
entry_block.first_fnum / 65536, entry_block.entries.size());
for (auto se16 : entry_block.entries) {
if (line_entries == 0) {
format("$1$, $2$,", se16.skipmap, se16.field_entry_offset);
++line_entries;
} else if (line_entries < 5) {
format(" $1$, $2$,", se16.skipmap, se16.field_entry_offset);
++line_entries;
} else {
format(" $1$, $2$,\n", se16.skipmap, se16.field_entry_offset);
line_entries = 0;
}
}
}
if (line_entries) format("\n");
format("65535, 65535\n");
}
if (ordered_fields_.empty()) {
GOOGLE_LOG_IF(DFATAL, !tc_table_info_->aux_entries.empty())
<< "Invalid message: " << descriptor_->full_name() << " has "
<< tc_table_info_->aux_entries.size()
<< " auxiliary field entries, but no fields";
format(
"}},\n"
"// no field_entries, or aux_entries\n"
"{{\n");
} else {
format("}}, {{\n");
{
// field_entries[]
auto field_scope = format.ScopedIndent();
GenerateFieldEntries(format);
}
if (tc_table_info_->aux_entries.empty()) {
format(
"}},\n"
"// no aux_entries\n"
"{{\n");
} else {
format("}}, {{\n");
{
// aux_entries[]
auto aux_scope = format.ScopedIndent();
for (const auto& aux_entry : tc_table_info_->aux_entries) {
switch (aux_entry.type) {
case TailCallTableInfo::kNothing:
format("{},\n");
break;
case TailCallTableInfo::kInlinedStringDonatedOffset:
format(
"{_fl::Offset{offsetof($classname$, "
"_impl_._inlined_string_donated_)}},\n");
break;
case TailCallTableInfo::kSplitOffset:
format(
"{_fl::Offset{offsetof($classname$, _impl_._split_)}},\n");
break;
case TailCallTableInfo::kSplitSizeof:
format("{_fl::Offset{sizeof($classname$::Impl_::Split)}},\n");
break;
case TailCallTableInfo::kSubMessage:
format("{::_pbi::FieldAuxDefaultMessage{}, &$1$},\n",
QualifiedDefaultInstanceName(
aux_entry.field->message_type(), options_));
break;
case TailCallTableInfo::kSubTable:
format("{::_pbi::TcParser::GetTable<$1$>()},\n",
QualifiedClassName(aux_entry.field->message_type(),
options_));
break;
case TailCallTableInfo::kSubMessageWeak:
format("{::_pbi::FieldAuxDefaultMessage{}, &$1$},\n",
QualifiedDefaultInstancePtr(
aux_entry.field->message_type(), options_));
break;
case TailCallTableInfo::kMessageVerifyFunc:
if (aux_entry.field != nullptr) {
format("{$1$::InternalVerify},\n",
QualifiedClassName(aux_entry.field->message_type(),
options_));
} else {
format("{},\n");
}
break;
case TailCallTableInfo::kEnumRange:
format("{$1$, $2$},\n", aux_entry.enum_range.start,
aux_entry.enum_range.size);
break;
case TailCallTableInfo::kEnumValidator:
format(
"{$1$_IsValid},\n",
QualifiedClassName(aux_entry.field->enum_type(), options_));
break;
case TailCallTableInfo::kNumericOffset:
format("{_fl::Offset{$1$}},\n", aux_entry.offset);
break;
}
}
}
format("}}, {{\n");
}
} // ordered_fields_.empty()
{
// field_names[]
auto field_name_scope = format.ScopedIndent();
GenerateFieldNames(format);
}
format("}},\n");
}
format("};\n\n"); // _table_
}
void ParseFunctionGenerator::GenerateFastFieldEntries(Formatter& format) {
for (const auto& info : tc_table_info_->fast_path_fields) {
if (info.field != nullptr) {
PrintFieldComment(format, info.field);
}
if (info.func_name.empty()) {
format("{::_pbi::TcParser::MiniParse, {}},\n");
} else if (info.field == nullptr) {
// Fast slot that is not associated with a field. Eg end group tags.
format("{$1$, {$2$, $3$}},\n", info.func_name, info.coded_tag,
info.nonfield_info);
} else {
GOOGLE_CHECK(!ShouldSplit(info.field, options_));
std::string func_name = info.func_name;
if (GetOptimizeFor(info.field->file(), options_) == FileOptions::SPEED) {
// For 1-byte tags we have a more optimized version of the varint parser
// that can hardcode the offset and has bit.
if (absl::EndsWith(func_name, "V8S1") ||
absl::EndsWith(func_name, "V32S1") ||
absl::EndsWith(func_name, "V64S1")) {
std::string field_type = absl::EndsWith(func_name, "V8S1") ? "bool"
: absl::EndsWith(func_name, "V32S1")
? "::uint32_t"
: "::uint64_t";
func_name = absl::StrCat(
"::_pbi::TcParser::SingularVarintNoZag1<", field_type,
", offsetof(", //
ClassName(info.field->containing_type()), //
", ", //
FieldMemberName(info.field, /*split=*/false), //
"), ", //
info.hasbit_idx, //
">()");
}
}
format(
"{$1$,\n"
" {$2$, $3$, $4$, PROTOBUF_FIELD_OFFSET($classname$, $5$)}},\n",
func_name, info.coded_tag, info.hasbit_idx, info.aux_idx,
FieldMemberName(info.field, /*split=*/false));
}
}
}
static void FormatFieldKind(Formatter& format,
const TailCallTableInfo::FieldEntryInfo& entry) {
// In here we convert the runtime value of entry.type_card back into a
// sequence of literal enum labels. We use the mnenonic labels for nicer
// codegen.
namespace fl = internal::field_layout;
const uint16_t type_card = entry.type_card;
const int rep_index = (type_card & fl::kRepMask) >> fl::kRepShift;
const int tv_index = (type_card & fl::kTvMask) >> fl::kTvShift;
format("(");
static constexpr const char* kFieldCardNames[] = {"Singular", "Optional",
"Repeated", "Oneof"};
static_assert((fl::kFcSingular >> fl::kFcShift) == 0, "");
static_assert((fl::kFcOptional >> fl::kFcShift) == 1, "");
static_assert((fl::kFcRepeated >> fl::kFcShift) == 2, "");
static_assert((fl::kFcOneof >> fl::kFcShift) == 3, "");
format("::_fl::kFc$1$",
kFieldCardNames[(type_card & fl::kFcMask) >> fl::kFcShift]);
#define PROTOBUF_INTERNAL_TYPE_CARD_CASE(x) \
case fl::k##x: \
format(" | ::_fl::k" #x); \
break
switch (type_card & fl::kFkMask) {
case fl::kFkString: {
switch (type_card & ~fl::kFcMask & ~fl::kRepMask & ~fl::kSplitMask) {
PROTOBUF_INTERNAL_TYPE_CARD_CASE(Bytes);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(RawString);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(Utf8String);
default:
GOOGLE_LOG(FATAL) << "Unknown type_card: 0x" << type_card;
}
static constexpr const char* kRepNames[] = {"AString", "IString", "Cord",
"SPiece", "SString"};
static_assert((fl::kRepAString >> fl::kRepShift) == 0, "");
static_assert((fl::kRepIString >> fl::kRepShift) == 1, "");
static_assert((fl::kRepCord >> fl::kRepShift) == 2, "");
static_assert((fl::kRepSPiece >> fl::kRepShift) == 3, "");
static_assert((fl::kRepSString >> fl::kRepShift) == 4, "");
format(" | ::_fl::kRep$1$", kRepNames[rep_index]);
break;
}
case fl::kFkMessage: {
format(" | ::_fl::kMessage");
static constexpr const char* kRepNames[] = {nullptr, "Group", "Lazy"};
static_assert((fl::kRepGroup >> fl::kRepShift) == 1, "");
static_assert((fl::kRepLazy >> fl::kRepShift) == 2, "");
if (auto* rep = kRepNames[rep_index]) {
format(" | ::_fl::kRep$1$", rep);
}
static constexpr const char* kXFormNames[2][4] = {
{nullptr, "Default", "Table", "WeakPtr"}, {nullptr, "Eager", "Lazy"}};
static_assert((fl::kTvDefault >> fl::kTvShift) == 1, "");
static_assert((fl::kTvTable >> fl::kTvShift) == 2, "");
static_assert((fl::kTvWeakPtr >> fl::kTvShift) == 3, "");
static_assert((fl::kTvEager >> fl::kTvShift) == 1, "");
static_assert((fl::kTvLazy >> fl::kTvShift) == 2, "");
if (auto* xform = kXFormNames[rep_index == 2][tv_index]) {
format(" | ::_fl::kTv$1$", xform);
}
break;
}
case fl::kFkMap:
format(" | ::_fl::kMap");
break;
case fl::kFkNone:
break;
case fl::kFkVarint:
case fl::kFkPackedVarint:
case fl::kFkFixed:
case fl::kFkPackedFixed: {
switch (type_card & ~fl::kFcMask & ~fl::kSplitMask) {
PROTOBUF_INTERNAL_TYPE_CARD_CASE(Bool);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(Fixed32);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(UInt32);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(SFixed32);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(Int32);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(SInt32);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(Float);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(Enum);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(EnumRange);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(OpenEnum);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(Fixed64);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(UInt64);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(SFixed64);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(Int64);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(SInt64);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(Double);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedBool);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedFixed32);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedUInt32);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedSFixed32);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedInt32);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedSInt32);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedFloat);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedEnum);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedEnumRange);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedOpenEnum);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedFixed64);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedUInt64);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedSFixed64);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedInt64);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedSInt64);
PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedDouble);
default:
GOOGLE_LOG(FATAL) << "Unknown type_card: 0x" << type_card;
}
}
}
if (type_card & fl::kSplitMask) {
format(" | ::_fl::kSplitTrue");
}
#undef PROTOBUF_INTERNAL_TYPE_CARD_CASE
format(")");
}
void ParseFunctionGenerator::GenerateFieldEntries(Formatter& format) {
for (const auto& entry : tc_table_info_->field_entries) {
const FieldDescriptor* field = entry.field;
PrintFieldComment(format, field);
format("{");
if (IsWeak(field, options_)) {
// Weak fields are handled by the generated fallback function.
// (These are handled by legacy Google-internal logic.)
format("/* weak */ 0, 0, 0, 0");
} else {
const OneofDescriptor* oneof = field->real_containing_oneof();
bool split = ShouldSplit(field, options_);
if (split) {
format("PROTOBUF_FIELD_OFFSET($classname$::Impl_::Split, $1$), ",
FieldName(field) + "_");
} else {
format("PROTOBUF_FIELD_OFFSET($classname$, $1$), ",
FieldMemberName(field, /*cold=*/false));
}
if (oneof) {
format("_Internal::kOneofCaseOffset + $1$, ", 4 * oneof->index());
} else if (num_hasbits_ > 0 || IsMapEntryMessage(descriptor_)) {
if (entry.hasbit_idx >= 0) {
format("_Internal::kHasBitsOffset + $1$, ", entry.hasbit_idx);
} else {
format("$1$, ", entry.hasbit_idx);
}
} else {
format("0, ");
}
format("$1$,\n ", entry.aux_idx);
FormatFieldKind(format, entry);
}
format("},\n");
}
}
void ParseFunctionGenerator::GenerateFieldNames(Formatter& format) {
if (tc_table_info_->field_name_data.empty()) {
// No names to output.
return;
}
// We could just output the bytes directly, but we want it to look better than
// that in the source code. Also, it is more efficient for compilation time to
// have a literal string than an initializer list of chars.
const int total_sizes =
static_cast<int>(((tc_table_info_->field_entries.size() + 1) + 7) & ~7);
const uint8_t* p = tc_table_info_->field_name_data.data();
const uint8_t* sizes = p;
const uint8_t* sizes_end = sizes + total_sizes;
// First print all the sizes as octal
format("\"");
for (int i = 0; i < total_sizes; ++i) {
int size = *p++;
int octal_size = ((size >> 6) & 3) * 100 + //
((size >> 3) & 7) * 10 + //
((size >> 0) & 7);
format("\\$1$", octal_size);
}
format("\"\n");
// Then print each name in a line of its own
for (; sizes < sizes_end; p += *sizes++) {
if (*sizes != 0) format("\"$1$\"\n", std::string(p, p + *sizes));
}
}
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_)) {
GOOGLE_DCHECK(!inlined_string_indices_.empty());
int inlined_string_index = inlined_string_indices_[field->index()];
GOOGLE_DCHECK_GT(inlined_string_index, 0);
format(", &$msg$$inlined_string_donated_array$[0], $1$, $this$",
inlined_string_index);
} else {
GOOGLE_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 (!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;
}
format(
"auto str = $msg$$1$$2$_$name$();\n"
"ptr = ::_pbi::Inline$3$(str, ptr, ctx);\n",
HasInternalAccessors(ctype) ? "_internal_" : "",
field->is_repeated() && !field->is_packable() ? "add" : "mutable",
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();
GOOGLE_CHECK(val);
if (val->type() == FieldDescriptor::TYPE_ENUM &&
!internal::cpp::HasPreservingUnknownEnumSemantics(field)) {
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_)) {
format(
"ctx->set_lazy_eager_verify_func($1$);\n",
eager_verify
? absl::StrCat("&", ClassName(field->message_type(), true),
"::InternalVerify")
: "nullptr");
}
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"
" ::google::protobuf::internal::LazyVerifyOption::$2$,\n"
" lazy_field);\n"
"ptr = ctx->ParseMessage(&parse_helper, ptr);\n",
FieldMessageTypeName(field, options_),
eager_verify ? "kEager" : "kLazy");
if (ShouldVerify(descriptor_, options_, scc_analyzer_) &&
eager_verify) {
format("ctx->set_lazy_eager_verify_func(nullptr);\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<const ::$proto_ns$::MessageLite*>($1$ptr_)"
"), ptr);\n",
QualifiedDefaultInstanceName(field->message_type(), options_));
}
} else if (IsWeak(field, options_)) {
format(
"{\n"
" auto* default_ = &reinterpret_cast<const Message&>($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:
GOOGLE_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()) {
format.AddMap({{"put_field", absl::StrCat("add_", FieldName(field))},
{"mutable_field", absl::StrCat("add_", FieldName(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(
"$uint32$ 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<int>(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: {
GOOGLE_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);
GOOGLE_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<const FieldDescriptor*>& 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, static_cast<WireFormatLite::WireType>(0));
uint32_t end_tag = WireFormatLite::MakeTag(
range->end, static_cast<WireFormatLite::WireType>(0));
if (range->end > 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<const FieldDescriptor*>& 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);
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");
}
#if 0
void PopulateFastFieldEntry(const Descriptor* descriptor,
const TailCallTableInfo::FieldEntryInfo& entry,
const Options& options,
TailCallTableInfo::FastFieldInfo& info) {
.....
if (name == "V8S1") {
info.func_name = absl::StrCat(
"::_pbi::TcParser::SingularVarintNoZag1<bool, offsetof(", //
ClassName(descriptor), //
", ", //
FieldMemberName(field, /*split=*/false), //
"), ", //
HasHasbit(field) ? entry.hasbit_idx : 63, //
">()");
} else if (name == "V32S1") {
info.func_name = absl::StrCat(
"::_pbi::TcParser::SingularVarintNoZag1<::uint32_t, offsetof(", //
ClassName(descriptor), //
", ", //
FieldMemberName(field, /*split=*/false), //
"), ", //
HasHasbit(field) ? entry.hasbit_idx : 63, //
">()");
} else if (name == "V64S1") {
info.func_name = absl::StrCat(
"::_pbi::TcParser::SingularVarintNoZag1<::uint64_t, offsetof(", //
ClassName(descriptor), //
", ", //
FieldMemberName(field, /*split=*/false), //
"), ", //
HasHasbit(field) ? entry.hasbit_idx : 63, //
">()");
} else {
info.func_name = absl::StrCat("::_pbi::TcParser::Fast", name);
}
info.aux_idx = aux_idx;
}
#endif
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google