• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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