• 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 // This file contains declarations needed in generated headers for messages
9 // that use tail-call table parsing. Everything in this file is for internal
10 // use only.
11 
12 #ifndef GOOGLE_PROTOBUF_GENERATED_MESSAGE_TCTABLE_DECL_H__
13 #define GOOGLE_PROTOBUF_GENERATED_MESSAGE_TCTABLE_DECL_H__
14 
15 #include <array>
16 #include <atomic>
17 #include <cstddef>
18 #include <cstdint>
19 #include <type_traits>
20 
21 #include "absl/log/absl_check.h"
22 #include "absl/types/span.h"
23 #include "google/protobuf/arena.h"
24 #include "google/protobuf/message_lite.h"
25 #include "google/protobuf/parse_context.h"
26 #include "google/protobuf/port.h"
27 
28 // Must come last:
29 #include "google/protobuf/port_def.inc"
30 
31 namespace google {
32 namespace protobuf {
33 namespace internal {
34 
35 // Additional information about this field:
36 struct TcFieldData {
TcFieldDataTcFieldData37   constexpr TcFieldData() : data(0) {}
TcFieldDataTcFieldData38   explicit constexpr TcFieldData(uint64_t data) : data(data) {}
39 
40   // Fast table entry constructor:
TcFieldDataTcFieldData41   constexpr TcFieldData(uint16_t coded_tag, uint8_t hasbit_idx, uint8_t aux_idx,
42                         uint16_t offset)
43       : data(uint64_t{offset} << 48 |      //
44              uint64_t{aux_idx} << 24 |     //
45              uint64_t{hasbit_idx} << 16 |  //
46              uint64_t{coded_tag}) {}
47 
48   // Constructor to create an explicit 'uninitialized' instance.
49   // This constructor can be used to pass an uninitialized `data` value to a
50   // table driven parser function that does not use `data`. The purpose of this
51   // is that it allows the compiler to reallocate and re-purpose the register
52   // that is currently holding its value for other data. This reduces register
53   // allocations inside the highly optimized varint parsing functions.
54   //
55   // Applications not using `data` use the `PROTOBUF_TC_PARAM_NO_DATA_DECL`
56   // macro to declare the standard input arguments with no name for the `data`
57   // argument. Callers then use the `PROTOBUF_TC_PARAM_NO_DATA_PASS` macro.
58   //
59   // Example:
60   //   if (ptr == nullptr) {
61   //      PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
62   //   }
63   struct DefaultInit {};
TcFieldDataTcFieldData64   TcFieldData(DefaultInit) {}  // NOLINT(google-explicit-constructor)
65 
66   // Fields used in fast table parsing:
67   //
68   //     Bit:
69   //     +-----------+-------------------+
70   //     |63    ..     32|31     ..     0|
71   //     +---------------+---------------+
72   //     :   .   :   .   :   . 16|=======| [16] coded_tag()
73   //     :   .   :   .   : 24|===|   .   : [ 8] hasbit_idx()
74   //     :   .   :   . 32|===|   :   .   : [ 8] aux_idx()
75   //     :   . 48:---.---:   .   :   .   : [16] (unused)
76   //     |=======|   .   :   .   :   .   : [16] offset()
77   //     +-----------+-------------------+
78   //     |63    ..     32|31     ..     0|
79   //     +---------------+---------------+
80 
81   template <typename TagType = uint16_t>
coded_tagTcFieldData82   TagType coded_tag() const {
83     return static_cast<TagType>(data);
84   }
hasbit_idxTcFieldData85   uint8_t hasbit_idx() const { return static_cast<uint8_t>(data >> 16); }
aux_idxTcFieldData86   uint8_t aux_idx() const { return static_cast<uint8_t>(data >> 24); }
offsetTcFieldData87   uint16_t offset() const { return static_cast<uint16_t>(data >> 48); }
88 
89   // Constructor for special entries that do not represent a field.
90   //  - End group: `nonfield_info` is the decoded tag.
TcFieldDataTcFieldData91   constexpr TcFieldData(uint16_t coded_tag, uint16_t nonfield_info)
92       : data(uint64_t{nonfield_info} << 16 |  //
93              uint64_t{coded_tag}) {}
94 
95   // Fields used in non-field entries
96   //
97   //     Bit:
98   //     +-----------+-------------------+
99   //     |63    ..     32|31     ..     0|
100   //     +---------------+---------------+
101   //     :   .   :   .   :   . 16|=======| [16] coded_tag()
102   //     :   .   :   . 32|=======|   .   : [16] decoded_tag()
103   //     :---.---:---.---:   .   :   .   : [32] (unused)
104   //     +-----------+-------------------+
105   //     |63    ..     32|31     ..     0|
106   //     +---------------+---------------+
107 
decoded_tagTcFieldData108   uint16_t decoded_tag() const { return static_cast<uint16_t>(data >> 16); }
109 
110   // Fields used in mini table parsing:
111   //
112   //     Bit:
113   //     +-----------+-------------------+
114   //     |63    ..     32|31     ..     0|
115   //     +---------------+---------------+
116   //     :   .   :   .   |===============| [32] tag() (decoded)
117   //     |===============|   .   :   .   : [32] entry_offset()
118   //     +-----------+-------------------+
119   //     |63    ..     32|31     ..     0|
120   //     +---------------+---------------+
121 
tagTcFieldData122   uint32_t tag() const { return static_cast<uint32_t>(data); }
entry_offsetTcFieldData123   uint32_t entry_offset() const { return static_cast<uint32_t>(data >> 32); }
124 
125   union {
126     uint64_t data;
127   };
128 };
129 
130 struct TcParseTableBase;
131 
132 // TailCallParseFunc is the function pointer type used in the tailcall table.
133 typedef PROTOBUF_CC const char* (*TailCallParseFunc)(PROTOBUF_TC_PARAM_DECL);
134 
135 namespace field_layout {
136 struct Offset {
137   uint32_t off;
138 };
139 }  // namespace field_layout
140 
141 #if defined(_MSC_VER) && !defined(_WIN64)
142 #pragma warning(push)
143 // TcParseTableBase is intentionally overaligned on 32 bit targets.
144 #pragma warning(disable : 4324)
145 #endif
146 
147 struct FieldAuxDefaultMessage {};
148 struct FieldAuxEnumData {};
149 
150 // Small type card used by mini parse to handle map entries.
151 // Map key/values are very limited, so we can encode the whole thing in a single
152 // byte.
153 class MapTypeCard {
154  public:
155   enum CppType { kBool, k32, k64, kString, kMessage };
156   MapTypeCard() = default;
MapTypeCard(WireFormatLite::WireType wiretype,CppType cpp_type,bool is_zigzag_utf8,bool is_signed)157   constexpr MapTypeCard(WireFormatLite::WireType wiretype, CppType cpp_type,
158                         bool is_zigzag_utf8, bool is_signed)
159       : data_(static_cast<uint8_t>((static_cast<uint8_t>(wiretype) << 0) |
160                                    (static_cast<uint8_t>(cpp_type) << 3) |
161                                    (static_cast<uint8_t>(is_zigzag_utf8) << 6) |
162                                    (static_cast<uint8_t>(is_signed) << 7))) {}
163 
wiretype()164   WireFormatLite::WireType wiretype() const {
165     return static_cast<WireFormatLite::WireType>((data_ >> 0) & 0x7);
166   }
167 
cpp_type()168   CppType cpp_type() const { return static_cast<CppType>((data_ >> 3) & 0x7); }
169 
is_signed()170   bool is_signed() const {
171     ABSL_DCHECK(cpp_type() == CppType::k32 || cpp_type() == CppType::k64);
172     return static_cast<bool>(data_ >> 7);
173   }
174 
is_zigzag()175   bool is_zigzag() const {
176     ABSL_DCHECK(wiretype() == WireFormatLite::WIRETYPE_VARINT);
177     ABSL_DCHECK(cpp_type() == CppType::k32 || cpp_type() == CppType::k64);
178     return is_zigzag_utf8();
179   }
is_utf8()180   bool is_utf8() const {
181     ABSL_DCHECK(wiretype() == WireFormatLite::WIRETYPE_LENGTH_DELIMITED);
182     ABSL_DCHECK(cpp_type() == CppType::kString);
183     return is_zigzag_utf8();
184   }
185 
186  private:
is_zigzag_utf8()187   bool is_zigzag_utf8() const { return static_cast<bool>((data_ >> 6) & 0x1); }
188   uint8_t data_;
189 };
190 static_assert(sizeof(MapTypeCard) == sizeof(uint8_t), "");
191 
192 // Make the map entry type card for a specified field type.
MakeMapTypeCard(WireFormatLite::FieldType type)193 constexpr MapTypeCard MakeMapTypeCard(WireFormatLite::FieldType type) {
194   switch (type) {
195     case WireFormatLite::TYPE_FLOAT:
196       return {WireFormatLite::WIRETYPE_FIXED32, MapTypeCard::k32, false, true};
197     case WireFormatLite::TYPE_FIXED32:
198       return {WireFormatLite::WIRETYPE_FIXED32, MapTypeCard::k32, false, false};
199     case WireFormatLite::TYPE_SFIXED32:
200       return {WireFormatLite::WIRETYPE_FIXED32, MapTypeCard::k32, false, true};
201 
202     case WireFormatLite::TYPE_DOUBLE:
203       return {WireFormatLite::WIRETYPE_FIXED64, MapTypeCard::k64, false, true};
204     case WireFormatLite::TYPE_FIXED64:
205       return {WireFormatLite::WIRETYPE_FIXED64, MapTypeCard::k64, false, false};
206     case WireFormatLite::TYPE_SFIXED64:
207       return {WireFormatLite::WIRETYPE_FIXED64, MapTypeCard::k64, false, true};
208 
209     case WireFormatLite::TYPE_BOOL:
210       return {WireFormatLite::WIRETYPE_VARINT, MapTypeCard::kBool, false,
211               false};
212 
213     case WireFormatLite::TYPE_ENUM:
214       // Enum validation is handled via `value_is_validated_enum` below.
215       return {WireFormatLite::WIRETYPE_VARINT, MapTypeCard::k32, false, true};
216     case WireFormatLite::TYPE_INT32:
217       return {WireFormatLite::WIRETYPE_VARINT, MapTypeCard::k32, false, true};
218     case WireFormatLite::TYPE_UINT32:
219       return {WireFormatLite::WIRETYPE_VARINT, MapTypeCard::k32, false, false};
220 
221     case WireFormatLite::TYPE_INT64:
222       return {WireFormatLite::WIRETYPE_VARINT, MapTypeCard::k64, false, true};
223     case WireFormatLite::TYPE_UINT64:
224       return {WireFormatLite::WIRETYPE_VARINT, MapTypeCard::k64, false, false};
225 
226     case WireFormatLite::TYPE_SINT32:
227       return {WireFormatLite::WIRETYPE_VARINT, MapTypeCard::k32, true, true};
228     case WireFormatLite::TYPE_SINT64:
229       return {WireFormatLite::WIRETYPE_VARINT, MapTypeCard::k64, true, true};
230 
231     case WireFormatLite::TYPE_STRING:
232       return {WireFormatLite::WIRETYPE_LENGTH_DELIMITED, MapTypeCard::kString,
233               true, false};
234     case WireFormatLite::TYPE_BYTES:
235       return {WireFormatLite::WIRETYPE_LENGTH_DELIMITED, MapTypeCard::kString,
236               false, false};
237 
238     case WireFormatLite::TYPE_MESSAGE:
239       return {WireFormatLite::WIRETYPE_LENGTH_DELIMITED, MapTypeCard::kMessage,
240               false, false};
241 
242     case WireFormatLite::TYPE_GROUP:
243     default:
244       Unreachable();
245   }
246 }
247 
248 enum class MapNodeSizeInfoT : uint32_t;
249 
250 // Aux entry for map fields.
251 struct MapAuxInfo {
252   MapTypeCard key_type_card;
253   MapTypeCard value_type_card;
254   // When off, we fall back to table->fallback to handle the parse. An example
255   // of this is for DynamicMessage.
256   uint8_t is_supported : 1;
257   // Determines if we are using LITE or the full runtime. When using the full
258   // runtime we have to synchronize with reflection before accessing the map.
259   uint8_t use_lite : 1;
260   // If true UTF8 errors cause the parsing to fail.
261   uint8_t fail_on_utf8_failure : 1;
262   // If true UTF8 errors are logged, but they are accepted.
263   uint8_t log_debug_utf8_failure : 1;
264   // If true the next aux contains the enum validator.
265   uint8_t value_is_validated_enum : 1;
266   // Size information derived from the actual node type.
267   MapNodeSizeInfoT node_size_info;
268 };
269 static_assert(sizeof(MapAuxInfo) <= 8, "");
270 
271 // Base class for message-level table with info for the tail-call parser.
272 struct alignas(uint64_t) TcParseTableBase {
273   // Common attributes for message layout:
274   uint16_t has_bits_offset;
275   uint16_t extension_offset;
276   uint32_t max_field_number;
277   uint8_t fast_idx_mask;
278   // Testing one bit is cheaper than testing whether post_loop_handler is null,
279   // and we expect it to be null most of the time so no reason to load the
280   // pointer.
281   uint8_t has_post_loop_handler : 1;
282   uint16_t lookup_table_offset;
283   uint32_t skipmap32;
284   uint32_t field_entries_offset;
285   uint16_t num_field_entries;
286 
287   uint16_t num_aux_entries;
288   uint32_t aux_offset;
289 
290   const ClassData* class_data;
291   using PostLoopHandler = const char* (*)(MessageLite* msg, const char* ptr,
292                                           ParseContext* ctx);
293   PostLoopHandler post_loop_handler;
294 
295   // Handler for fields which are not handled by table dispatch.
296   TailCallParseFunc fallback;
297 
298   // A sub message's table to be prefetched.
299 #ifdef PROTOBUF_PREFETCH_PARSE_TABLE
300   const TcParseTableBase* to_prefetch;
301 #endif  // PROTOBUF_PREFETCH_PARSE_TABLE
302 
303   // This constructor exactly follows the field layout, so it's technically
304   // not necessary.  However, it makes it much much easier to add or re-arrange
305   // fields, because it can be overloaded with an additional constructor,
306   // temporarily allowing both old and new protocol buffer headers to be
307   // compiled.
TcParseTableBaseTcParseTableBase308   constexpr TcParseTableBase(uint16_t has_bits_offset,
309                              uint16_t extension_offset,
310                              uint32_t max_field_number, uint8_t fast_idx_mask,
311                              uint16_t lookup_table_offset, uint32_t skipmap32,
312                              uint32_t field_entries_offset,
313                              uint16_t num_field_entries,
314                              uint16_t num_aux_entries, uint32_t aux_offset,
315                              const ClassData* class_data,
316                              PostLoopHandler post_loop_handler,
317                              TailCallParseFunc fallback
318 #ifdef PROTOBUF_PREFETCH_PARSE_TABLE
319                              ,
320                              const TcParseTableBase* to_prefetch
321 #endif  // PROTOBUF_PREFETCH_PARSE_TABLE
322                              )
323       : has_bits_offset(has_bits_offset),
324         extension_offset(extension_offset),
325         max_field_number(max_field_number),
326         fast_idx_mask(fast_idx_mask),
327         has_post_loop_handler(post_loop_handler != nullptr),
328         lookup_table_offset(lookup_table_offset),
329         skipmap32(skipmap32),
330         field_entries_offset(field_entries_offset),
331         num_field_entries(num_field_entries),
332         num_aux_entries(num_aux_entries),
333         aux_offset(aux_offset),
334         class_data(class_data),
335         post_loop_handler(post_loop_handler),
336         fallback(fallback)
337 #ifdef PROTOBUF_PREFETCH_PARSE_TABLE
338         ,
339         to_prefetch(to_prefetch)
340 #endif  // PROTOBUF_PREFETCH_PARSE_TABLE
341   {
342   }
343 
344   // Table entry for fast-path tailcall dispatch handling.
345   struct FastFieldEntry {
346     // Target function for dispatch:
347     mutable std::atomic<TailCallParseFunc> target_atomic;
348 
349     // Field data used during parse:
350     TcFieldData bits;
351 
352     // Default initializes this instance with undefined values.
353     FastFieldEntry() = default;
354 
355     // Constant initializes this instance
FastFieldEntryTcParseTableBase::FastFieldEntry356     constexpr FastFieldEntry(TailCallParseFunc func, TcFieldData bits)
357         : target_atomic(func), bits(bits) {}
358 
359     // FastFieldEntry is copy-able and assignable, which is intended
360     // mainly for testing and debugging purposes.
FastFieldEntryTcParseTableBase::FastFieldEntry361     FastFieldEntry(const FastFieldEntry& rhs) noexcept
362         : FastFieldEntry(rhs.target(), rhs.bits) {}
363     FastFieldEntry& operator=(const FastFieldEntry& rhs) noexcept {
364       SetTarget(rhs.target());
365       bits = rhs.bits;
366       return *this;
367     }
368 
369     // Protocol buffer code should use these relaxed accessors.
targetTcParseTableBase::FastFieldEntry370     TailCallParseFunc target() const {
371       return target_atomic.load(std::memory_order_relaxed);
372     }
SetTargetTcParseTableBase::FastFieldEntry373     void SetTarget(TailCallParseFunc func) const {
374       return target_atomic.store(func, std::memory_order_relaxed);
375     }
376   };
377   // There is always at least one table entry.
fast_entryTcParseTableBase378   const FastFieldEntry* fast_entry(size_t idx) const {
379     return reinterpret_cast<const FastFieldEntry*>(this + 1) + idx;
380   }
fast_entryTcParseTableBase381   FastFieldEntry* fast_entry(size_t idx) {
382     return reinterpret_cast<FastFieldEntry*>(this + 1) + idx;
383   }
384 
385   // Returns a begin iterator (pointer) to the start of the field lookup table.
field_lookup_beginTcParseTableBase386   const uint16_t* field_lookup_begin() const {
387     return reinterpret_cast<const uint16_t*>(reinterpret_cast<uintptr_t>(this) +
388                                              lookup_table_offset);
389   }
field_lookup_beginTcParseTableBase390   uint16_t* field_lookup_begin() {
391     return reinterpret_cast<uint16_t*>(reinterpret_cast<uintptr_t>(this) +
392                                        lookup_table_offset);
393   }
394 
395   // Field entry for all fields.
396   struct FieldEntry {
397     uint32_t offset;     // offset in the message object
398     int32_t has_idx;     // has-bit index, relative to the message object
399     uint16_t aux_idx;    // index for `field_aux`.
400     uint16_t type_card;  // `FieldType` and `Cardinality` (see _impl.h)
401 
402     static constexpr uint16_t kNoAuxIdx = 0xFFFF;
403   };
404 
405   // Returns a begin iterator (pointer) to the start of the field entries array.
field_entries_beginTcParseTableBase406   const FieldEntry* field_entries_begin() const {
407     return reinterpret_cast<const FieldEntry*>(
408         reinterpret_cast<uintptr_t>(this) + field_entries_offset);
409   }
field_entriesTcParseTableBase410   absl::Span<const FieldEntry> field_entries() const {
411     return {field_entries_begin(), num_field_entries};
412   }
field_entries_beginTcParseTableBase413   FieldEntry* field_entries_begin() {
414     return reinterpret_cast<FieldEntry*>(reinterpret_cast<uintptr_t>(this) +
415                                          field_entries_offset);
416   }
417 
418   // Auxiliary entries for field types that need extra information.
419   union FieldAux {
FieldAux()420     constexpr FieldAux() : message_default_p(nullptr) {}
FieldAux(FieldAuxEnumData,const uint32_t * enum_data)421     constexpr FieldAux(FieldAuxEnumData, const uint32_t* enum_data)
422         : enum_data(enum_data) {}
FieldAux(field_layout::Offset off)423     constexpr FieldAux(field_layout::Offset off) : offset(off.off) {}
FieldAux(int16_t range_start,uint16_t range_length)424     constexpr FieldAux(int16_t range_start, uint16_t range_length)
425         : enum_range{range_start, range_length} {}
FieldAux(const MessageLite * msg)426     constexpr FieldAux(const MessageLite* msg) : message_default_p(msg) {}
FieldAux(FieldAuxDefaultMessage,const void * msg)427     constexpr FieldAux(FieldAuxDefaultMessage, const void* msg)
428         : message_default_p(msg) {}
FieldAux(const TcParseTableBase * table)429     constexpr FieldAux(const TcParseTableBase* table) : table(table) {}
FieldAux(MapAuxInfo map_info)430     constexpr FieldAux(MapAuxInfo map_info) : map_info(map_info) {}
FieldAux(LazyEagerVerifyFnType verify_func)431     constexpr FieldAux(LazyEagerVerifyFnType verify_func)
432         : verify_func(verify_func) {}
433     struct {
434       int16_t start;    // minimum enum number (if it fits)
435       uint16_t length;  // length of range (i.e., max = start + length - 1)
436     } enum_range;
437     uint32_t offset;
438     const void* message_default_p;
439     const uint32_t* enum_data;
440     const TcParseTableBase* table;
441     MapAuxInfo map_info;
442     LazyEagerVerifyFnType verify_func;
443 
message_default()444     const MessageLite* message_default() const {
445       return static_cast<const MessageLite*>(message_default_p);
446     }
message_default_weak()447     const MessageLite* message_default_weak() const {
448       return *static_cast<const MessageLite* const*>(message_default_p);
449     }
450   };
field_auxTcParseTableBase451   const FieldAux* field_aux(uint32_t idx) const {
452     return reinterpret_cast<const FieldAux*>(reinterpret_cast<uintptr_t>(this) +
453                                              aux_offset) +
454            idx;
455   }
field_auxTcParseTableBase456   FieldAux* field_aux(uint32_t idx) {
457     return reinterpret_cast<FieldAux*>(reinterpret_cast<uintptr_t>(this) +
458                                        aux_offset) +
459            idx;
460   }
field_auxTcParseTableBase461   const FieldAux* field_aux(const FieldEntry* entry) const {
462     return field_aux(entry->aux_idx);
463   }
464 
465   // Field name data
name_dataTcParseTableBase466   const char* name_data() const {
467     return reinterpret_cast<const char*>(reinterpret_cast<uintptr_t>(this) +
468                                          aux_offset +
469                                          num_aux_entries * sizeof(FieldAux));
470   }
name_dataTcParseTableBase471   char* name_data() {
472     return reinterpret_cast<char*>(reinterpret_cast<uintptr_t>(this) +
473                                    aux_offset +
474                                    num_aux_entries * sizeof(FieldAux));
475   }
476 
default_instanceTcParseTableBase477   const MessageLite* default_instance() const { return class_data->prototype; }
478 };
479 
480 #if defined(_MSC_VER) && !defined(_WIN64)
481 #pragma warning(pop)
482 #endif
483 
484 static_assert(sizeof(TcParseTableBase::FastFieldEntry) <= 16,
485               "Fast field entry is too big.");
486 static_assert(sizeof(TcParseTableBase::FieldEntry) <= 16,
487               "Field entry is too big.");
488 
489 template <size_t kFastTableSizeLog2, size_t kNumFieldEntries = 0,
490           size_t kNumFieldAux = 0, size_t kNameTableSize = 0,
491           size_t kFieldLookupSize = 2>
492 struct TcParseTable {
493   TcParseTableBase header;
494 
495   // Entries for each field.
496   //
497   // Fields are indexed by the lowest bits of their field number. The field
498   // number is masked to fit inside the table. Note that the parsing logic
499   // generally calls `TailCallParseTableBase::fast_entry()` instead of accessing
500   // this field directly.
501   std::array<TcParseTableBase::FastFieldEntry, (1 << kFastTableSizeLog2)>
502       fast_entries;
503 
504   // Just big enough to find all the field entries.
505   std::array<uint16_t, kFieldLookupSize> field_lookup_table;
506   // Entries for all fields:
507   std::array<TcParseTableBase::FieldEntry, kNumFieldEntries> field_entries;
508   std::array<TcParseTableBase::FieldAux, kNumFieldAux> aux_entries;
509   std::array<char, kNameTableSize == 0 ? 1 : kNameTableSize> field_names;
510 };
511 
512 // Partial specialization: if there are no aux entries, there will be no array.
513 // In C++, arrays cannot have length 0, but (C++11) std::array<T, 0> is valid.
514 // However, different implementations have different sizeof(std::array<T, 0>).
515 // Skipping the member makes offset computations portable.
516 template <size_t kFastTableSizeLog2, size_t kNumFieldEntries,
517           size_t kNameTableSize, size_t kFieldLookupSize>
518 struct TcParseTable<kFastTableSizeLog2, kNumFieldEntries, 0, kNameTableSize,
519                     kFieldLookupSize> {
520   TcParseTableBase header;
521   std::array<TcParseTableBase::FastFieldEntry, (1 << kFastTableSizeLog2)>
522       fast_entries;
523   std::array<uint16_t, kFieldLookupSize> field_lookup_table;
524   std::array<TcParseTableBase::FieldEntry, kNumFieldEntries> field_entries;
525   std::array<char, kNameTableSize == 0 ? 1 : kNameTableSize> field_names;
526 };
527 
528 // Partial specialization: if there are no fields at all, then we can save space
529 // by skipping the field numbers and entries.
530 template <size_t kNameTableSize, size_t kFieldLookupSize>
531 struct TcParseTable<0, 0, 0, kNameTableSize, kFieldLookupSize> {
532   TcParseTableBase header;
533   // N.B.: the fast entries are sized by log2, so 2**0 fields = 1 entry.
534   // The fast parsing loop will always use this entry, so it must be present.
535   std::array<TcParseTableBase::FastFieldEntry, 1> fast_entries;
536   std::array<uint16_t, kFieldLookupSize> field_lookup_table;
537   std::array<char, kNameTableSize == 0 ? 1 : kNameTableSize> field_names;
538 };
539 
540 static_assert(std::is_standard_layout<TcParseTable<1>>::value,
541               "TcParseTable must be standard layout.");
542 
543 static_assert(offsetof(TcParseTable<1>, fast_entries) ==
544                   sizeof(TcParseTableBase),
545               "Table entries must be laid out after TcParseTableBase.");
546 
547 template <typename T,
548           PROTOBUF_CC const char* (*func)(T*, const char*, ParseContext*)>
549 PROTOBUF_CC const char* StubParseImpl(PROTOBUF_TC_PARAM_DECL) {
550   return func(static_cast<T*>(msg), ptr, ctx);
551 }
552 
553 template <typename T,
554           PROTOBUF_CC const char* (*func)(T*, const char*, ParseContext*)>
555 constexpr TcParseTable<0> CreateStubTcParseTable(
556     const ClassData* class_data,
557     TcParseTableBase::PostLoopHandler post_loop_handler = nullptr) {
558   return {
559       {
560           0,                  // has_bits_offset
561           0,                  // extension_offset
562           0,                  // max_field_number
563           0,                  // fast_idx_mask
564           0,                  // lookup_table_offset
565           0,                  // skipmap32
566           0,                  // field_entries_offset
567           0,                  // num_field_entries
568           0,                  // num_aux_entries
569           0,                  // aux_offset
570           class_data,         //
571           post_loop_handler,  //
572           nullptr,            // fallback
573 #ifdef PROTOBUF_PREFETCH_PARSE_TABLE
574           nullptr,  // to_prefetch
575 #endif              // PROTOBUF_PREFETCH_PARSE_TABLE
576       },
577       {{{StubParseImpl<T, func>, {}}}},
578   };
579 }
580 
581 }  // namespace internal
582 }  // namespace protobuf
583 }  // namespace google
584 
585 #include "google/protobuf/port_undef.inc"
586 
587 #endif  // GOOGLE_PROTOBUF_GENERATED_MESSAGE_TCTABLE_DECL_H__
588