1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #include "google/protobuf/compiler/cpp/parse_function_generator.h"
9
10 #include <algorithm>
11 #include <cstddef>
12 #include <cstdint>
13 #include <memory>
14 #include <string>
15 #include <vector>
16
17 #include "absl/container/flat_hash_map.h"
18 #include "absl/log/absl_check.h"
19 #include "absl/log/absl_log.h"
20 #include "absl/strings/match.h"
21 #include "absl/strings/str_cat.h"
22 #include "absl/strings/string_view.h"
23 #include "google/protobuf/compiler/cpp/helpers.h"
24 #include "google/protobuf/compiler/cpp/options.h"
25 #include "google/protobuf/descriptor.h"
26 #include "google/protobuf/generated_message_tctable_gen.h"
27 #include "google/protobuf/generated_message_tctable_impl.h"
28
29 namespace google {
30 namespace protobuf {
31 namespace compiler {
32 namespace cpp {
33
34 namespace {
35 using internal::TailCallTableInfo;
36 using internal::cpp::Utf8CheckMode;
37
GetOrderedFields(const Descriptor * descriptor,const Options & options)38 std::vector<const FieldDescriptor*> GetOrderedFields(
39 const Descriptor* descriptor, const Options& options) {
40 std::vector<const FieldDescriptor*> ordered_fields;
41 for (auto field : FieldRange(descriptor)) {
42 ordered_fields.push_back(field);
43 }
44 std::sort(ordered_fields.begin(), ordered_fields.end(),
45 [](const FieldDescriptor* a, const FieldDescriptor* b) {
46 return a->number() < b->number();
47 });
48 return ordered_fields;
49 }
50
51 } // namespace
52
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,int index_in_file_messages)53 ParseFunctionGenerator::ParseFunctionGenerator(
54 const Descriptor* descriptor, int max_has_bit_index,
55 const std::vector<int>& has_bit_indices,
56 const std::vector<int>& inlined_string_indices, const Options& options,
57 MessageSCCAnalyzer* scc_analyzer,
58 const absl::flat_hash_map<absl::string_view, std::string>& vars,
59 int index_in_file_messages)
60 : descriptor_(descriptor),
61 scc_analyzer_(scc_analyzer),
62 options_(options),
63 variables_(vars),
64 inlined_string_indices_(inlined_string_indices),
65 ordered_fields_(GetOrderedFields(descriptor_, options_)),
66 num_hasbits_(max_has_bit_index),
67 index_in_file_messages_(index_in_file_messages) {
68 std::vector<TailCallTableInfo::FieldOptions> fields;
69 fields.reserve(ordered_fields_.size());
70 for (size_t i = 0; i < ordered_fields_.size(); ++i) {
71 auto* field = ordered_fields_[i];
72 ABSL_CHECK_GE(field->index(), 0);
73 size_t index = static_cast<size_t>(field->index());
74 fields.push_back({
75 field,
76 index < has_bit_indices.size() ? has_bit_indices[index] : -1,
77 GetPresenceProbability(field, options_),
78 GetLazyStyle(field, options_, scc_analyzer_),
79 IsStringInlined(field, options_),
80 IsImplicitWeakField(field, options_, scc_analyzer_),
81 /* use_direct_tcparser_table */ true,
82 ShouldSplit(field, options_),
83 index < inlined_string_indices.size() ? inlined_string_indices[index]
84 : -1,
85 });
86 }
87 tc_table_info_ = std::make_unique<TailCallTableInfo>(
88 descriptor_,
89 TailCallTableInfo::MessageOptions{
90 /* is_lite */ GetOptimizeFor(descriptor->file(), options_) ==
91 FileOptions::LITE_RUNTIME,
92 /* uses_codegen */ true,
93 options_.profile_driven_cluster_aux_subtable},
94 fields);
95 SetCommonMessageDataVariables(descriptor_, &variables_);
96 SetUnknownFieldsVariable(descriptor_, options_, &variables_);
97 variables_["classname"] = ClassName(descriptor, false);
98 }
99
100 struct SkipEntry16 {
101 uint16_t skipmap;
102 uint16_t field_entry_offset;
103 };
104 struct SkipEntryBlock {
105 uint32_t first_fnum;
106 std::vector<SkipEntry16> entries;
107 };
108 struct NumToEntryTable {
109 uint32_t skipmap32; // for fields #1 - #32
110 std::vector<SkipEntryBlock> blocks;
111 // Compute the number of uint16_t required to represent this table.
size16google::protobuf::compiler::cpp::NumToEntryTable112 int size16() const {
113 int size = 2; // for the termination field#
114 for (const auto& block : blocks) {
115 // 2 for the field#, 1 for a count of skip entries, 2 for each entry.
116 size += 3 + block.entries.size() * 2;
117 }
118 return size;
119 }
120 };
121
122 static NumToEntryTable MakeNumToEntryTable(
123 const std::vector<const FieldDescriptor*>& field_descriptors);
124
FieldNameDataSize(const std::vector<uint8_t> & data)125 static int FieldNameDataSize(const std::vector<uint8_t>& data) {
126 // We add a +1 here to allow for a NUL termination character. It makes the
127 // codegen nicer.
128 return data.empty() ? 0 : static_cast<int>(data.size()) + 1;
129 }
130
GenerateDataDecls(io::Printer * p)131 void ParseFunctionGenerator::GenerateDataDecls(io::Printer* p) {
132 auto v = p->WithVars(variables_);
133 auto field_num_to_entry_table = MakeNumToEntryTable(ordered_fields_);
134 p->Emit(
135 {
136 {"SECTION",
137 [&] {
138 if (!IsProfileDriven(options_)) return;
139 std::string section_name;
140 // Since most (>80%) messages are never present, messages that are
141 // present are considered hot enough to be clustered together.
142 // When using weak descriptors we use unique sections for each
143 // table to allow for GC to work. pth/ptl names must be in sync
144 // with the linker script.
145 if (UsingImplicitWeakDescriptor(descriptor_->file(), options_)) {
146 section_name = WeakDescriptorDataSection(
147 IsPresentMessage(descriptor_, options_) ? "pth" : "ptl",
148 descriptor_, index_in_file_messages_, options_);
149 } else if (IsPresentMessage(descriptor_, options_)) {
150 section_name = "proto_parse_table_hot";
151 } else {
152 section_name = "proto_parse_table_lukewarm";
153 }
154 p->Emit({{"section_name", section_name}},
155 "ABSL_ATTRIBUTE_SECTION_VARIABLE($section_name$)");
156 }},
157 {"table_size_log2", tc_table_info_->table_size_log2},
158 {"num_field_entries", ordered_fields_.size()},
159 {"num_field_aux", tc_table_info_->aux_entries.size()},
160 {"name_table_size",
161 FieldNameDataSize(tc_table_info_->field_name_data)},
162 {"field_lookup_size", field_num_to_entry_table.size16()},
163 },
164 R"cc(
165 friend class ::$proto_ns$::internal::TcParser;
166 $SECTION$
167 static const ::$proto_ns$::internal::TcParseTable<
168 $table_size_log2$, $num_field_entries$, $num_field_aux$,
169 $name_table_size$, $field_lookup_size$>
170 _table_;
171 )cc");
172 }
173
GenerateDataDefinitions(io::Printer * printer)174 void ParseFunctionGenerator::GenerateDataDefinitions(io::Printer* printer) {
175 GenerateTailCallTable(printer);
176 }
177
MakeNumToEntryTable(const std::vector<const FieldDescriptor * > & field_descriptors)178 static NumToEntryTable MakeNumToEntryTable(
179 const std::vector<const FieldDescriptor*>& field_descriptors) {
180 NumToEntryTable num_to_entry_table;
181 num_to_entry_table.skipmap32 = static_cast<uint32_t>(-1);
182
183 // skip_entry_block is the current block of SkipEntries that we're
184 // appending to. cur_block_first_fnum is the number of the first
185 // field represented by the block.
186 uint16_t field_entry_index = 0;
187 uint16_t N = field_descriptors.size();
188 // First, handle field numbers 1-32, which affect only the initial
189 // skipmap32 and don't generate additional skip-entry blocks.
190 for (; field_entry_index != N; ++field_entry_index) {
191 auto* field_descriptor = field_descriptors[field_entry_index];
192 if (field_descriptor->number() > 32) break;
193 auto skipmap32_index = field_descriptor->number() - 1;
194 num_to_entry_table.skipmap32 -= 1 << skipmap32_index;
195 }
196 // If all the field numbers were less than or equal to 32, we will have
197 // no further entries to process, and we are already done.
198 if (field_entry_index == N) return num_to_entry_table;
199
200 SkipEntryBlock* block = nullptr;
201 bool start_new_block = true;
202 // To determine sparseness, track the field number corresponding to
203 // the start of the most recent skip entry.
204 uint32_t last_skip_entry_start = 0;
205 for (; field_entry_index != N; ++field_entry_index) {
206 auto* field_descriptor = field_descriptors[field_entry_index];
207 uint32_t fnum = static_cast<uint32_t>(field_descriptor->number());
208 ABSL_CHECK_GT(fnum, last_skip_entry_start);
209 if (start_new_block == false) {
210 // If the next field number is within 15 of the last_skip_entry_start, we
211 // continue writing just to that entry. If it's between 16 and 31 more,
212 // then we just extend the current block by one. If it's more than 31
213 // more, we have to add empty skip entries in order to continue using the
214 // existing block. Obviously it's just 32 more, it doesn't make sense to
215 // start a whole new block, since new blocks mean having to write out
216 // their starting field number, which is 32 bits, as well as the size of
217 // the additional block, which is 16... while an empty SkipEntry16 only
218 // costs 32 bits. So if it was 48 more, it's a slight space win; we save
219 // 16 bits, but probably at the cost of slower run time. We're choosing
220 // 96 for now.
221 if (fnum - last_skip_entry_start > 96) start_new_block = true;
222 }
223 if (start_new_block) {
224 num_to_entry_table.blocks.push_back(SkipEntryBlock{fnum});
225 block = &num_to_entry_table.blocks.back();
226 start_new_block = false;
227 }
228
229 auto skip_entry_num = (fnum - block->first_fnum) / 16;
230 auto skip_entry_index = (fnum - block->first_fnum) % 16;
231 while (skip_entry_num >= block->entries.size())
232 block->entries.push_back({0xFFFF, field_entry_index});
233 block->entries[skip_entry_num].skipmap -= 1 << (skip_entry_index);
234
235 last_skip_entry_start = fnum - skip_entry_index;
236 }
237 return num_to_entry_table;
238 }
239
TcParseFunctionName(internal::TcParseFunction func)240 static std::string TcParseFunctionName(internal::TcParseFunction func) {
241 #define PROTOBUF_TC_PARSE_FUNCTION_X(value) #value,
242 static constexpr absl::string_view kNames[] = {
243 {}, PROTOBUF_TC_PARSE_FUNCTION_LIST};
244 #undef PROTOBUF_TC_PARSE_FUNCTION_X
245 const int func_index = static_cast<int>(func);
246 ABSL_CHECK_GE(func_index, 0);
247 ABSL_CHECK_LT(func_index, std::end(kNames) - std::begin(kNames));
248 static constexpr absl::string_view ns = "::_pbi::TcParser::";
249 return absl::StrCat(ns, kNames[func_index]);
250 }
251
GenerateTailCallTable(io::Printer * printer)252 void ParseFunctionGenerator::GenerateTailCallTable(io::Printer* printer) {
253 Formatter format(printer, variables_);
254 // For simplicity and speed, the table is not covering all proto
255 // configurations. This model uses a fallback to cover all situations that
256 // the table can't accommodate, together with unknown fields or extensions.
257 // These are number of fields over 32, fields with 3 or more tag bytes,
258 // maps, weak fields, lazy, more than 1 extension range. In the cases
259 // the table is sufficient we can use a generic routine, that just handles
260 // unknown fields and potentially an extension range.
261 auto field_num_to_entry_table = MakeNumToEntryTable(ordered_fields_);
262 format(
263 "$1$ ::_pbi::TcParseTable<$2$, $3$, $4$, $5$, $6$> "
264 "$classname$::_table_ = "
265 "{\n",
266 // FileDescriptorProto's table must be constant initialized. For MSVC this
267 // means using `constexpr`. However, we can't use `constexpr` for all
268 // tables because it breaks when crossing DLL boundaries.
269 // FileDescriptorProto is safe from this.
270 IsFileDescriptorProto(descriptor_->file(), options_)
271 ? "constexpr"
272 : "PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1\nconst",
273 tc_table_info_->table_size_log2, ordered_fields_.size(),
274 tc_table_info_->aux_entries.size(),
275 FieldNameDataSize(tc_table_info_->field_name_data),
276 field_num_to_entry_table.size16());
277 {
278 auto table_scope = format.ScopedIndent();
279 format("{\n");
280 {
281 auto header_scope = format.ScopedIndent();
282 if (num_hasbits_ > 0 || IsMapEntryMessage(descriptor_)) {
283 format("PROTOBUF_FIELD_OFFSET($classname$, _impl_._has_bits_),\n");
284 } else {
285 format("0, // no _has_bits_\n");
286 }
287 if (descriptor_->extension_range_count() != 0) {
288 format("PROTOBUF_FIELD_OFFSET($classname$, $extensions$),\n");
289 } else {
290 format("0, // no _extensions_\n");
291 }
292 format("$1$, $2$, // max_field_number, fast_idx_mask\n",
293 (ordered_fields_.empty() ? 0 : ordered_fields_.back()->number()),
294 (((1 << tc_table_info_->table_size_log2) - 1) << 3));
295 format(
296 "offsetof(decltype(_table_), field_lookup_table),\n"
297 "$1$, // skipmap\n",
298 field_num_to_entry_table.skipmap32);
299 if (ordered_fields_.empty()) {
300 format(
301 "offsetof(decltype(_table_), field_names), // no field_entries\n");
302 } else {
303 format("offsetof(decltype(_table_), field_entries),\n");
304 }
305
306 format(
307 "$1$, // num_field_entries\n"
308 "$2$, // num_aux_entries\n",
309 ordered_fields_.size(), tc_table_info_->aux_entries.size());
310 if (tc_table_info_->aux_entries.empty()) {
311 format(
312 "offsetof(decltype(_table_), field_names), // no aux_entries\n");
313 } else {
314 format("offsetof(decltype(_table_), aux_entries),\n");
315 }
316 format("_class_data_.base(),\n");
317 if (NeedsPostLoopHandler(descriptor_, options_)) {
318 printer->Emit(R"cc(
319 &$classname$::PostLoopHandler,
320 )cc");
321 } else {
322 printer->Emit(R"cc(
323 nullptr, // post_loop_handler
324 )cc");
325 }
326 format("$1$, // fallback\n",
327 TcParseFunctionName(tc_table_info_->fallback_function));
328 std::vector<const FieldDescriptor*> subtable_fields;
329 for (const auto& aux : tc_table_info_->aux_entries) {
330 if (aux.type == internal::TailCallTableInfo::kSubTable) {
331 subtable_fields.push_back(aux.field);
332 }
333 }
334 const auto* hottest = FindHottestField(subtable_fields, options_);
335 // We'll prefetch `to_prefetch->to_prefetch` unconditionally to avoid
336 // branches. Set the pointer to itself to avoid nullptr.
337 printer->Emit(
338 {{"hottest_type_name",
339 QualifiedClassName(
340 hottest == nullptr ? descriptor_ : hottest->message_type(),
341 options_)}},
342 // clang-format off
343 R"cc(
344 #ifdef PROTOBUF_PREFETCH_PARSE_TABLE
345 ::_pbi::TcParser::GetTable<$hottest_type_name$>(), // to_prefetch
346 #endif // PROTOBUF_PREFETCH_PARSE_TABLE
347 )cc");
348 // clang-format on
349 }
350 format("}, {{\n");
351 {
352 // fast_entries[]
353 auto fast_scope = format.ScopedIndent();
354 GenerateFastFieldEntries(format);
355 }
356 format("}}, {{\n");
357 {
358 // field_lookup_table[]
359 auto field_lookup_scope = format.ScopedIndent();
360 int line_entries = 0;
361 for (auto& entry_block : field_num_to_entry_table.blocks) {
362 format("$1$, $2$, $3$,\n", entry_block.first_fnum & 65535,
363 entry_block.first_fnum / 65536, entry_block.entries.size());
364 for (auto se16 : entry_block.entries) {
365 if (line_entries == 0) {
366 format("$1$, $2$,", se16.skipmap, se16.field_entry_offset);
367 ++line_entries;
368 } else if (line_entries < 5) {
369 format(" $1$, $2$,", se16.skipmap, se16.field_entry_offset);
370 ++line_entries;
371 } else {
372 format(" $1$, $2$,\n", se16.skipmap, se16.field_entry_offset);
373 line_entries = 0;
374 }
375 }
376 }
377 if (line_entries) format("\n");
378 format("65535, 65535\n");
379 }
380 if (ordered_fields_.empty() &&
381 !descriptor_->options().message_set_wire_format()) {
382 ABSL_DLOG_IF(FATAL, !tc_table_info_->aux_entries.empty())
383 << "Invalid message: " << descriptor_->full_name() << " has "
384 << tc_table_info_->aux_entries.size()
385 << " auxiliary field entries, but no fields";
386 format(
387 "}},\n"
388 "// no field_entries, or aux_entries\n"
389 "{{\n");
390 } else {
391 format("}}, {{\n");
392 {
393 // field_entries[]
394 auto field_scope = format.ScopedIndent();
395 GenerateFieldEntries(format);
396 }
397 if (tc_table_info_->aux_entries.empty()) {
398 format(
399 "}},\n"
400 "// no aux_entries\n"
401 "{{\n");
402 } else {
403 format("}}, {{\n");
404 {
405 // aux_entries[]
406 auto aux_scope = format.ScopedIndent();
407 for (const auto& aux_entry : tc_table_info_->aux_entries) {
408 switch (aux_entry.type) {
409 case TailCallTableInfo::kNothing:
410 format("{},\n");
411 break;
412 case TailCallTableInfo::kInlinedStringDonatedOffset:
413 format(
414 "{_fl::Offset{offsetof($classname$, "
415 "_impl_._inlined_string_donated_)}},\n");
416 break;
417 case TailCallTableInfo::kSplitOffset:
418 format(
419 "{_fl::Offset{offsetof($classname$, _impl_._split_)}},\n");
420 break;
421 case TailCallTableInfo::kSplitSizeof:
422 format("{_fl::Offset{sizeof($classname$::Impl_::Split)}},\n");
423 break;
424 case TailCallTableInfo::kSubMessage:
425 format("{::_pbi::FieldAuxDefaultMessage{}, &$1$},\n",
426 QualifiedDefaultInstanceName(
427 aux_entry.field->message_type(), options_));
428 break;
429 case TailCallTableInfo::kSubTable:
430 format("{::_pbi::TcParser::GetTable<$1$>()},\n",
431 QualifiedClassName(aux_entry.field->message_type(),
432 options_));
433 break;
434 case TailCallTableInfo::kSubMessageWeak:
435 format("{::_pbi::FieldAuxDefaultMessage{}, &$1$},\n",
436 QualifiedDefaultInstancePtr(
437 aux_entry.field->message_type(), options_));
438 break;
439 case TailCallTableInfo::kMessageVerifyFunc:
440 format("{$1$::InternalVerify},\n",
441 QualifiedClassName(aux_entry.field->message_type(),
442 options_));
443 break;
444 case TailCallTableInfo::kSelfVerifyFunc:
445 if (ShouldVerify(descriptor_, options_, scc_analyzer_)) {
446 format("{&InternalVerify},\n");
447 } else {
448 format("{},\n");
449 }
450 break;
451 case TailCallTableInfo::kEnumRange:
452 format("{$1$, $2$},\n", aux_entry.enum_range.start,
453 aux_entry.enum_range.size);
454 break;
455 case TailCallTableInfo::kEnumValidator:
456 format(
457 "{::_pbi::FieldAuxEnumData{}, $1$_internal_data_},\n",
458 QualifiedClassName(aux_entry.field->enum_type(), options_));
459 break;
460 case TailCallTableInfo::kNumericOffset:
461 format("{_fl::Offset{$1$}},\n", aux_entry.offset);
462 break;
463 case TailCallTableInfo::kMapAuxInfo: {
464 auto utf8_check = internal::cpp::GetUtf8CheckMode(
465 aux_entry.field,
466 GetOptimizeFor(aux_entry.field->file(), options_) ==
467 FileOptions::LITE_RUNTIME);
468 auto* map_key = aux_entry.field->message_type()->map_key();
469 auto* map_value = aux_entry.field->message_type()->map_value();
470 const bool validated_enum =
471 map_value->type() == FieldDescriptor::TYPE_ENUM &&
472 !internal::cpp::HasPreservingUnknownEnumSemantics(
473 map_value);
474 printer->Emit(
475 {
476 {"field", FieldMemberName(
477 aux_entry.field,
478 ShouldSplit(aux_entry.field, options_))},
479 {"strict", utf8_check == Utf8CheckMode::kStrict},
480 {"verify", utf8_check == Utf8CheckMode::kVerify},
481 {"validate", validated_enum},
482 {"key_wire", map_key->type()},
483 {"value_wire", map_value->type()},
484 },
485 R"cc(
486 {::_pbi::TcParser::GetMapAuxInfo<
487 decltype($classname$().$field$)>(
488 $strict$, $verify$, $validate$, $key_wire$,
489 $value_wire$)},
490 )cc");
491 break;
492 }
493 }
494 }
495 }
496 format("}}, {{\n");
497 }
498 } // ordered_fields_.empty()
499 {
500 // field_names[]
501 auto field_name_scope = format.ScopedIndent();
502 GenerateFieldNames(format);
503 }
504 format("}},\n");
505 }
506 format("};\n\n"); // _table_
507 }
508
GenerateFastFieldEntries(Formatter & format)509 void ParseFunctionGenerator::GenerateFastFieldEntries(Formatter& format) {
510 for (const auto& info : tc_table_info_->fast_path_fields) {
511 if (auto* nonfield = info.AsNonField()) {
512 // Fast slot that is not associated with a field. Eg end group tags.
513 format("{$1$, {$2$, $3$}},\n", TcParseFunctionName(nonfield->func),
514 nonfield->coded_tag, nonfield->nonfield_info);
515 } else if (auto* as_field = info.AsField()) {
516 PrintFieldComment(format, as_field->field, options_);
517 ABSL_CHECK(!ShouldSplit(as_field->field, options_));
518
519 std::string func_name = TcParseFunctionName(as_field->func);
520 if (GetOptimizeFor(as_field->field->file(), options_) ==
521 FileOptions::SPEED) {
522 // For 1-byte tags we have a more optimized version of the varint parser
523 // that can hardcode the offset and has bit.
524 if (absl::EndsWith(func_name, "V8S1") ||
525 absl::EndsWith(func_name, "V32S1") ||
526 absl::EndsWith(func_name, "V64S1")) {
527 std::string field_type = absl::EndsWith(func_name, "V8S1") ? "bool"
528 : absl::EndsWith(func_name, "V32S1")
529 ? "::uint32_t"
530 : "::uint64_t";
531 func_name = absl::StrCat(
532 "::_pbi::TcParser::SingularVarintNoZag1<", field_type,
533 ", offsetof(", //
534 ClassName(as_field->field->containing_type()), //
535 ", ", //
536 FieldMemberName(as_field->field, /*split=*/false), //
537 "), ", //
538 as_field->hasbit_idx, //
539 ">()");
540 }
541 }
542
543 format(
544 "{$1$,\n"
545 " {$2$, $3$, $4$, PROTOBUF_FIELD_OFFSET($classname$, $5$)}},\n",
546 func_name, as_field->coded_tag, as_field->hasbit_idx,
547 as_field->aux_idx, FieldMemberName(as_field->field, /*split=*/false));
548 } else {
549 ABSL_DCHECK(info.is_empty());
550 format("{::_pbi::TcParser::MiniParse, {}},\n");
551 }
552 }
553 }
554
GenerateFieldEntries(Formatter & format)555 void ParseFunctionGenerator::GenerateFieldEntries(Formatter& format) {
556 for (const auto& entry : tc_table_info_->field_entries) {
557 const FieldDescriptor* field = entry.field;
558 PrintFieldComment(format, field, options_);
559 format("{");
560 if (IsWeak(field, options_)) {
561 // Weak fields are handled by the reflection fallback function.
562 // (These are handled by legacy Google-internal logic.)
563 format("/* weak */ 0, 0, 0, 0");
564 } else {
565 const OneofDescriptor* oneof = field->real_containing_oneof();
566 bool split = ShouldSplit(field, options_);
567 if (split) {
568 format("PROTOBUF_FIELD_OFFSET($classname$::Impl_::Split, $1$), ",
569 absl::StrCat(FieldName(field), "_"));
570 } else {
571 format("PROTOBUF_FIELD_OFFSET($classname$, $1$), ",
572 FieldMemberName(field, /*split=*/false));
573 }
574 if (oneof) {
575 format("_Internal::kOneofCaseOffset + $1$, ", 4 * oneof->index());
576 } else if (num_hasbits_ > 0 || IsMapEntryMessage(descriptor_)) {
577 if (entry.hasbit_idx >= 0) {
578 format("_Internal::kHasBitsOffset + $1$, ", entry.hasbit_idx);
579 } else {
580 format("$1$, ", entry.hasbit_idx);
581 }
582 } else {
583 format("0, ");
584 }
585 format("$1$,\n ", entry.aux_idx);
586 // Use `0|` prefix to eagerly convert the enums to int to avoid enum-enum
587 // operations. They are deprecated in C++20.
588 format("(0 | $1$)", internal::TypeCardToString(entry.type_card));
589 }
590 format("},\n");
591 }
592 }
593
GenerateFieldNames(Formatter & format)594 void ParseFunctionGenerator::GenerateFieldNames(Formatter& format) {
595 if (tc_table_info_->field_name_data.empty()) {
596 // No names to output.
597 return;
598 }
599
600 // We could just output the bytes directly, but we want it to look better than
601 // that in the source code. Also, it is more efficient for compilation time to
602 // have a literal string than an initializer list of chars.
603
604 const int total_sizes =
605 static_cast<int>(((tc_table_info_->field_entries.size() + 1) + 7) & ~7u);
606 const uint8_t* p = tc_table_info_->field_name_data.data();
607 const uint8_t* sizes = p;
608 const uint8_t* sizes_end = sizes + total_sizes;
609
610 // First print all the sizes as octal
611 format("\"");
612 for (int i = 0; i < total_sizes; ++i) {
613 int size = *p++;
614 int octal_size = ((size >> 6) & 3) * 100 + //
615 ((size >> 3) & 7) * 10 + //
616 ((size >> 0) & 7);
617 format("\\$1$", octal_size);
618 }
619 format("\"\n");
620
621 // Then print each name in a line of its own
622 for (; sizes < sizes_end; p += *sizes++) {
623 if (*sizes != 0) format("\"$1$\"\n", std::string(p, p + *sizes));
624 }
625 }
626
627 } // namespace cpp
628 } // namespace compiler
629 } // namespace protobuf
630 } // namespace google
631