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