1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 #include <cstdint>
32 #include <numeric>
33
34 #include <google/protobuf/extension_set.h>
35 #include <google/protobuf/generated_message_tctable_decl.h>
36 #include <google/protobuf/generated_message_tctable_impl.h>
37 #include <google/protobuf/inlined_string_field.h>
38 #include <google/protobuf/message_lite.h>
39 #include <google/protobuf/parse_context.h>
40 #include <google/protobuf/wire_format_lite.h>
41
42 // clang-format off
43 #include <google/protobuf/port_def.inc>
44 // clang-format on
45
46 namespace google {
47 namespace protobuf {
48 namespace internal {
49
50 using FieldEntry = TcParseTableBase::FieldEntry;
51
52 //////////////////////////////////////////////////////////////////////////////
53 // Template instantiations:
54 //////////////////////////////////////////////////////////////////////////////
55
56 #ifndef NDEBUG
57 template void AlignFail<4>(uintptr_t);
58 template void AlignFail<8>(uintptr_t);
59 #endif
60
GenericFallbackLite(PROTOBUF_TC_PARAM_DECL)61 const char* TcParser::GenericFallbackLite(PROTOBUF_TC_PARAM_DECL) {
62 return GenericFallbackImpl<MessageLite, std::string>(PROTOBUF_TC_PARAM_PASS);
63 }
64
65 //////////////////////////////////////////////////////////////////////////////
66 // Core fast parsing implementation:
67 //////////////////////////////////////////////////////////////////////////////
68
69 class TcParser::ScopedArenaSwap final {
70 public:
ScopedArenaSwap(MessageLite * msg,ParseContext * ctx)71 ScopedArenaSwap(MessageLite* msg, ParseContext* ctx)
72 : ctx_(ctx), saved_(ctx->data().arena) {
73 ctx_->data().arena = msg->GetArenaForAllocation();
74 }
75 ScopedArenaSwap(const ScopedArenaSwap&) = delete;
~ScopedArenaSwap()76 ~ScopedArenaSwap() { ctx_->data().arena = saved_; }
77
78 private:
79 ParseContext* const ctx_;
80 Arena* const saved_;
81 };
82
ParseLoop(MessageLite * msg,const char * ptr,ParseContext * ctx,const TcParseTableBase * table)83 PROTOBUF_NOINLINE const char* TcParser::ParseLoop(
84 MessageLite* msg, const char* ptr, ParseContext* ctx,
85 const TcParseTableBase* table) {
86 ScopedArenaSwap saved(msg, ctx);
87 while (!ctx->Done(&ptr)) {
88 // Unconditionally read has bits, even if we don't have has bits.
89 // has_bits_offset will be 0 and we will just read something valid.
90 uint64_t hasbits = ReadAt<uint32_t>(msg, table->has_bits_offset);
91 ptr = TagDispatch(msg, ptr, ctx, table, hasbits, {});
92 if (ptr == nullptr) break;
93 if (ctx->LastTag() != 1) break; // Ended on terminating tag
94 }
95 return ptr;
96 }
97
98 // Dispatch to the designated parse function
TagDispatch(PROTOBUF_TC_PARAM_DECL)99 inline PROTOBUF_ALWAYS_INLINE const char* TcParser::TagDispatch(
100 PROTOBUF_TC_PARAM_DECL) {
101 const auto coded_tag = UnalignedLoad<uint16_t>(ptr);
102 const size_t idx = coded_tag & table->fast_idx_mask;
103 PROTOBUF_ASSUME((idx & 7) == 0);
104 auto* fast_entry = table->fast_entry(idx >> 3);
105 data = fast_entry->bits;
106 data.data ^= coded_tag;
107 PROTOBUF_MUSTTAIL return fast_entry->target(PROTOBUF_TC_PARAM_PASS);
108 }
109
110 // We can only safely call from field to next field if the call is optimized
111 // to a proper tail call. Otherwise we blow through stack. Clang and gcc
112 // reliably do this optimization in opt mode, but do not perform this in debug
113 // mode. Luckily the structure of the algorithm is such that it's always
114 // possible to just return and use the enclosing parse loop as a trampoline.
ToTagDispatch(PROTOBUF_TC_PARAM_DECL)115 inline PROTOBUF_ALWAYS_INLINE const char* TcParser::ToTagDispatch(
116 PROTOBUF_TC_PARAM_DECL) {
117 constexpr bool always_return = !PROTOBUF_TAILCALL;
118 if (always_return || !ctx->DataAvailable(ptr)) {
119 PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
120 }
121 PROTOBUF_MUSTTAIL return TagDispatch(PROTOBUF_TC_PARAM_PASS);
122 }
123
ToParseLoop(PROTOBUF_TC_PARAM_DECL)124 inline PROTOBUF_ALWAYS_INLINE const char* TcParser::ToParseLoop(
125 PROTOBUF_TC_PARAM_DECL) {
126 (void)data;
127 (void)ctx;
128 SyncHasbits(msg, hasbits, table);
129 return ptr;
130 }
131
Error(PROTOBUF_TC_PARAM_DECL)132 inline PROTOBUF_ALWAYS_INLINE const char* TcParser::Error(
133 PROTOBUF_TC_PARAM_DECL) {
134 (void)data;
135 (void)ctx;
136 (void)ptr;
137 SyncHasbits(msg, hasbits, table);
138 return nullptr;
139 }
140
141 // On the fast path, a (matching) 1-byte tag already has the decoded value.
FastDecodeTag(uint8_t coded_tag)142 static uint32_t FastDecodeTag(uint8_t coded_tag) {
143 return coded_tag;
144 }
145
146 // On the fast path, a (matching) 2-byte tag always needs to be decoded.
FastDecodeTag(uint16_t coded_tag)147 static uint32_t FastDecodeTag(uint16_t coded_tag) {
148 uint32_t result = coded_tag;
149 result += static_cast<int8_t>(coded_tag);
150 return result >> 1;
151 }
152
153 //////////////////////////////////////////////////////////////////////////////
154 // Core mini parsing implementation:
155 //////////////////////////////////////////////////////////////////////////////
156
157 // Field lookup table layout:
158 //
159 // Because it consists of a series of variable-length segments, the lookuup
160 // table is organized within an array of uint16_t, and each element is either
161 // a uint16_t or a uint32_t stored little-endian as a pair of uint16_t.
162 //
163 // Its fundamental building block maps 16 contiguously ascending field numbers
164 // to their locations within the field entry table:
165
166 struct SkipEntry16 {
167 uint16_t skipmap;
168 uint16_t field_entry_offset;
169 };
170
171 // The skipmap is a bitfield of which of those field numbers do NOT have a
172 // field entry. The lowest bit of the skipmap corresponds to the lowest of
173 // the 16 field numbers, so if a proto had only fields 1, 2, 3, and 7, the
174 // skipmap would contain 0b11111111'10111000.
175 //
176 // The field lookup table begins with a single 32-bit skipmap that maps the
177 // field numbers 1 through 32. This is because the majority of proto
178 // messages only contain fields numbered 1 to 32.
179 //
180 // The rest of the lookup table is a repeated series of
181 // { 32-bit field #, #SkipEntry16s, {SkipEntry16...} }
182 // That is, the next thing is a pair of uint16_t that form the next
183 // lowest field number that the lookup table handles. If this number is -1,
184 // that is the end of the table. Then there is a uint16_t that is
185 // the number of contiguous SkipEntry16 entries that follow, and then of
186 // course the SkipEntry16s themselves.
187
188 // Originally developed and tested at https://godbolt.org/z/vbc7enYcf
189
190 // Returns the address of the field for `tag` in the table's field entries.
191 // Returns nullptr if the field was not found.
FindFieldEntry(const TcParseTableBase * table,uint32_t field_num)192 const TcParseTableBase::FieldEntry* TcParser::FindFieldEntry(
193 const TcParseTableBase* table, uint32_t field_num) {
194 const FieldEntry* const field_entries = table->field_entries_begin();
195
196 uint32_t fstart = 1;
197 uint32_t adj_fnum = field_num - fstart;
198
199 if (PROTOBUF_PREDICT_TRUE(adj_fnum < 32)) {
200 uint32_t skipmap = table->skipmap32;
201 uint32_t skipbit = 1 << adj_fnum;
202 if (PROTOBUF_PREDICT_FALSE(skipmap & skipbit)) return nullptr;
203 skipmap &= skipbit - 1;
204 #if (__GNUC__ || __clang__) && __POPCNT__
205 // Note: here and below, skipmap typically has very few set bits
206 // (31 in the worst case, but usually zero) so a loop isn't that
207 // bad, and a compiler-generated popcount is typically only
208 // worthwhile if the processor itself has hardware popcount support.
209 adj_fnum -= __builtin_popcount(skipmap);
210 #else
211 while (skipmap) {
212 --adj_fnum;
213 skipmap &= skipmap - 1;
214 }
215 #endif
216 auto* entry = field_entries + adj_fnum;
217 PROTOBUF_ASSUME(entry != nullptr);
218 return entry;
219 }
220 const uint16_t* lookup_table = table->field_lookup_begin();
221 for (;;) {
222 #ifdef PROTOBUF_LITTLE_ENDIAN
223 memcpy(&fstart, lookup_table, sizeof(fstart));
224 #else
225 fstart = lookup_table[0] | (lookup_table[1] << 16);
226 #endif
227 lookup_table += sizeof(fstart) / sizeof(*lookup_table);
228 uint32_t num_skip_entries = *lookup_table++;
229 if (field_num < fstart) return nullptr;
230 adj_fnum = field_num - fstart;
231 uint32_t skip_num = adj_fnum / 16;
232 if (PROTOBUF_PREDICT_TRUE(skip_num < num_skip_entries)) {
233 // for each group of 16 fields we have:
234 // a bitmap of 16 bits
235 // a 16-bit field-entry offset for the first of them.
236 auto* skip_data = lookup_table + (adj_fnum / 16) * (sizeof(SkipEntry16) /
237 sizeof(uint16_t));
238 SkipEntry16 se = {skip_data[0], skip_data[1]};
239 adj_fnum &= 15;
240 uint32_t skipmap = se.skipmap;
241 uint16_t skipbit = 1 << adj_fnum;
242 if (PROTOBUF_PREDICT_FALSE(skipmap & skipbit)) return nullptr;
243 skipmap &= skipbit - 1;
244 adj_fnum += se.field_entry_offset;
245 #if (__GNUC__ || __clang__) && __POPCNT__
246 adj_fnum -= __builtin_popcount(skipmap);
247 #else
248 while (skipmap) {
249 --adj_fnum;
250 skipmap &= skipmap - 1;
251 }
252 #endif
253 auto* entry = field_entries + adj_fnum;
254 PROTOBUF_ASSUME(entry != nullptr);
255 return entry;
256 }
257 lookup_table +=
258 num_skip_entries * (sizeof(SkipEntry16) / sizeof(*lookup_table));
259 }
260 }
261
262 // Field names are stored in a format of:
263 //
264 // 1) A table of name sizes, one byte each, from 1 to 255 per name.
265 // `entries` is the size of this first table.
266 // 1a) padding bytes, so the table of name sizes is a multiple of
267 // eight bytes in length. They are zero.
268 //
269 // 2) All the names, concatenated, with neither separation nor termination.
270 //
271 // This is designed to be compact but not particularly fast to retrieve.
272 // In particular, it takes O(n) to retrieve the name of the n'th field,
273 // which is usually fine because most protos have fewer than 10 fields.
FindName(const char * name_data,size_t entries,size_t index)274 static StringPiece FindName(const char* name_data, size_t entries,
275 size_t index) {
276 // The compiler unrolls these... if this isn't fast enough,
277 // there's an AVX version at https://godbolt.org/z/eojrjqzfr
278 // ARM-compatible version at https://godbolt.org/z/n5YT5Ee85
279
280 // The field name sizes are padded up to a multiple of 8, so we
281 // must pad them here.
282 size_t num_sizes = (entries + 7) & -8;
283 auto* uint8s = reinterpret_cast<const uint8_t*>(name_data);
284 size_t pos = std::accumulate(uint8s, uint8s + index, num_sizes);
285 size_t size = name_data[index];
286 auto* start = &name_data[pos];
287 return {start, size};
288 }
289
MessageName(const TcParseTableBase * table)290 StringPiece TcParser::MessageName(const TcParseTableBase* table) {
291 return FindName(table->name_data(), table->num_field_entries + 1, 0);
292 }
293
FieldName(const TcParseTableBase * table,const FieldEntry * field_entry)294 StringPiece TcParser::FieldName(const TcParseTableBase* table,
295 const FieldEntry* field_entry) {
296 const FieldEntry* const field_entries = table->field_entries_begin();
297 auto field_index = static_cast<size_t>(field_entry - field_entries);
298 return FindName(table->name_data(), table->num_field_entries + 1,
299 field_index + 1);
300 }
301
MiniParse(PROTOBUF_TC_PARAM_DECL)302 const char* TcParser::MiniParse(PROTOBUF_TC_PARAM_DECL) {
303 uint32_t tag;
304 ptr = ReadTagInlined(ptr, &tag);
305 if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) return nullptr;
306
307 auto* entry = FindFieldEntry(table, tag >> 3);
308 if (entry == nullptr) {
309 data.data = tag;
310 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
311 }
312
313 // The handler may need the tag and the entry to resolve fallback logic. Both
314 // of these are 32 bits, so pack them into (the 64-bit) `data`. Since we can't
315 // pack the entry pointer itself, just pack its offset from `table`.
316 uint64_t entry_offset = reinterpret_cast<const char*>(entry) -
317 reinterpret_cast<const char*>(table);
318 data.data = entry_offset << 32 | tag;
319
320 using field_layout::FieldKind;
321 auto field_type = entry->type_card & FieldKind::kFkMask;
322 switch (field_type) {
323 case FieldKind::kFkNone:
324 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
325 case FieldKind::kFkVarint:
326 PROTOBUF_MUSTTAIL return MpVarint(PROTOBUF_TC_PARAM_PASS);
327 case FieldKind::kFkPackedVarint:
328 PROTOBUF_MUSTTAIL return MpPackedVarint(PROTOBUF_TC_PARAM_PASS);
329 case FieldKind::kFkFixed:
330 PROTOBUF_MUSTTAIL return MpFixed(PROTOBUF_TC_PARAM_PASS);
331 case FieldKind::kFkPackedFixed:
332 PROTOBUF_MUSTTAIL return MpPackedFixed(PROTOBUF_TC_PARAM_PASS);
333 case FieldKind::kFkString:
334 PROTOBUF_MUSTTAIL return MpString(PROTOBUF_TC_PARAM_PASS);
335 case FieldKind::kFkMessage:
336 PROTOBUF_MUSTTAIL return MpMessage(PROTOBUF_TC_PARAM_PASS);
337 case FieldKind::kFkMap:
338 PROTOBUF_MUSTTAIL return MpMap(PROTOBUF_TC_PARAM_PASS);
339 default:
340 return Error(PROTOBUF_TC_PARAM_PASS);
341 }
342 }
343
344 namespace {
345
346 // Offset returns the address `offset` bytes after `base`.
Offset(void * base,uint32_t offset)347 inline void* Offset(void* base, uint32_t offset) {
348 return static_cast<uint8_t*>(base) + offset;
349 }
350
351 // InvertPacked changes tag bits from the given wire type to length
352 // delimited. This is the difference expected between packed and non-packed
353 // repeated fields.
354 template <WireFormatLite::WireType Wt>
InvertPacked(TcFieldData & data)355 inline PROTOBUF_ALWAYS_INLINE void InvertPacked(TcFieldData& data) {
356 data.data ^= Wt ^ WireFormatLite::WIRETYPE_LENGTH_DELIMITED;
357 }
358
359 } // namespace
360
361 //////////////////////////////////////////////////////////////////////////////
362 // Message fields
363 //////////////////////////////////////////////////////////////////////////////
364
365 template <typename TagType, bool group_coding>
366 inline PROTOBUF_ALWAYS_INLINE
SingularParseMessageAuxImpl(PROTOBUF_TC_PARAM_DECL)367 const char* TcParser::SingularParseMessageAuxImpl(PROTOBUF_TC_PARAM_DECL) {
368 if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
369 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
370 }
371 auto saved_tag = UnalignedLoad<TagType>(ptr);
372 ptr += sizeof(TagType);
373 hasbits |= (uint64_t{1} << data.hasbit_idx());
374 SyncHasbits(msg, hasbits, table);
375 auto& field = RefAt<MessageLite*>(msg, data.offset());
376 if (field == nullptr) {
377 const MessageLite* default_instance =
378 table->field_aux(data.aux_idx())->message_default;
379 field = default_instance->New(ctx->data().arena);
380 }
381 if (group_coding) {
382 return ctx->ParseGroup(field, ptr, FastDecodeTag(saved_tag));
383 }
384 return ctx->ParseMessage(field, ptr);
385 }
386
FastMS1(PROTOBUF_TC_PARAM_DECL)387 const char* TcParser::FastMS1(PROTOBUF_TC_PARAM_DECL) {
388 PROTOBUF_MUSTTAIL return SingularParseMessageAuxImpl<uint8_t, false>(
389 PROTOBUF_TC_PARAM_PASS);
390 }
391
FastMS2(PROTOBUF_TC_PARAM_DECL)392 const char* TcParser::FastMS2(PROTOBUF_TC_PARAM_DECL) {
393 PROTOBUF_MUSTTAIL return SingularParseMessageAuxImpl<uint16_t, false>(
394 PROTOBUF_TC_PARAM_PASS);
395 }
396
FastGS1(PROTOBUF_TC_PARAM_DECL)397 const char* TcParser::FastGS1(PROTOBUF_TC_PARAM_DECL) {
398 PROTOBUF_MUSTTAIL return SingularParseMessageAuxImpl<uint8_t, true>(
399 PROTOBUF_TC_PARAM_PASS);
400 }
401
FastGS2(PROTOBUF_TC_PARAM_DECL)402 const char* TcParser::FastGS2(PROTOBUF_TC_PARAM_DECL) {
403 PROTOBUF_MUSTTAIL return SingularParseMessageAuxImpl<uint16_t, true>(
404 PROTOBUF_TC_PARAM_PASS);
405 }
406
407 template <typename TagType, bool group_coding>
408 inline PROTOBUF_ALWAYS_INLINE
RepeatedParseMessageAuxImpl(PROTOBUF_TC_PARAM_DECL)409 const char* TcParser::RepeatedParseMessageAuxImpl(PROTOBUF_TC_PARAM_DECL) {
410 if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
411 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
412 }
413 auto saved_tag = UnalignedLoad<TagType>(ptr);
414 ptr += sizeof(TagType);
415 SyncHasbits(msg, hasbits, table);
416 const MessageLite* default_instance =
417 table->field_aux(data.aux_idx())->message_default;
418 auto& field = RefAt<RepeatedPtrFieldBase>(msg, data.offset());
419 MessageLite* submsg =
420 field.Add<GenericTypeHandler<MessageLite>>(default_instance);
421 if (group_coding) {
422 return ctx->ParseGroup(submsg, ptr, FastDecodeTag(saved_tag));
423 }
424 return ctx->ParseMessage(submsg, ptr);
425 }
426
FastMR1(PROTOBUF_TC_PARAM_DECL)427 const char* TcParser::FastMR1(PROTOBUF_TC_PARAM_DECL) {
428 PROTOBUF_MUSTTAIL return RepeatedParseMessageAuxImpl<uint8_t, false>(
429 PROTOBUF_TC_PARAM_PASS);
430 }
431
FastMR2(PROTOBUF_TC_PARAM_DECL)432 const char* TcParser::FastMR2(PROTOBUF_TC_PARAM_DECL) {
433 PROTOBUF_MUSTTAIL return RepeatedParseMessageAuxImpl<uint16_t, false>(
434 PROTOBUF_TC_PARAM_PASS);
435 }
436
FastGR1(PROTOBUF_TC_PARAM_DECL)437 const char* TcParser::FastGR1(PROTOBUF_TC_PARAM_DECL) {
438 PROTOBUF_MUSTTAIL return RepeatedParseMessageAuxImpl<uint8_t, true>(
439 PROTOBUF_TC_PARAM_PASS);
440 }
441
FastGR2(PROTOBUF_TC_PARAM_DECL)442 const char* TcParser::FastGR2(PROTOBUF_TC_PARAM_DECL) {
443 PROTOBUF_MUSTTAIL return RepeatedParseMessageAuxImpl<uint16_t, true>(
444 PROTOBUF_TC_PARAM_PASS);
445 }
446
447 //////////////////////////////////////////////////////////////////////////////
448 // Fixed fields
449 //////////////////////////////////////////////////////////////////////////////
450
451 template <typename LayoutType, typename TagType>
SingularFixed(PROTOBUF_TC_PARAM_DECL)452 PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularFixed(
453 PROTOBUF_TC_PARAM_DECL) {
454 if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
455 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
456 }
457 ptr += sizeof(TagType); // Consume tag
458 hasbits |= (uint64_t{1} << data.hasbit_idx());
459 RefAt<LayoutType>(msg, data.offset()) = UnalignedLoad<LayoutType>(ptr);
460 ptr += sizeof(LayoutType);
461 PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
462 }
463
FastF32S1(PROTOBUF_TC_PARAM_DECL)464 const char* TcParser::FastF32S1(PROTOBUF_TC_PARAM_DECL) {
465 PROTOBUF_MUSTTAIL return SingularFixed<uint32_t, uint8_t>(
466 PROTOBUF_TC_PARAM_PASS);
467 }
FastF32S2(PROTOBUF_TC_PARAM_DECL)468 const char* TcParser::FastF32S2(PROTOBUF_TC_PARAM_DECL) {
469 PROTOBUF_MUSTTAIL return SingularFixed<uint32_t, uint16_t>(
470 PROTOBUF_TC_PARAM_PASS);
471 }
FastF64S1(PROTOBUF_TC_PARAM_DECL)472 const char* TcParser::FastF64S1(PROTOBUF_TC_PARAM_DECL) {
473 PROTOBUF_MUSTTAIL return SingularFixed<uint64_t, uint8_t>(
474 PROTOBUF_TC_PARAM_PASS);
475 }
FastF64S2(PROTOBUF_TC_PARAM_DECL)476 const char* TcParser::FastF64S2(PROTOBUF_TC_PARAM_DECL) {
477 PROTOBUF_MUSTTAIL return SingularFixed<uint64_t, uint16_t>(
478 PROTOBUF_TC_PARAM_PASS);
479 }
480
481 template <typename LayoutType, typename TagType>
RepeatedFixed(PROTOBUF_TC_PARAM_DECL)482 PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedFixed(
483 PROTOBUF_TC_PARAM_DECL) {
484 if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
485 // Check if the field can be parsed as packed repeated:
486 constexpr WireFormatLite::WireType fallback_wt =
487 sizeof(LayoutType) == 4 ? WireFormatLite::WIRETYPE_FIXED32
488 : WireFormatLite::WIRETYPE_FIXED64;
489 InvertPacked<fallback_wt>(data);
490 if (data.coded_tag<TagType>() == 0) {
491 return PackedFixed<LayoutType, TagType>(PROTOBUF_TC_PARAM_PASS);
492 } else {
493 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
494 }
495 }
496 auto& field = RefAt<RepeatedField<LayoutType>>(msg, data.offset());
497 int idx = field.size();
498 auto elem = field.Add();
499 int space = field.Capacity() - idx;
500 idx = 0;
501 auto expected_tag = UnalignedLoad<TagType>(ptr);
502 do {
503 ptr += sizeof(TagType);
504 elem[idx++] = UnalignedLoad<LayoutType>(ptr);
505 ptr += sizeof(LayoutType);
506 if (idx >= space) break;
507 if (!ctx->DataAvailable(ptr)) break;
508 } while (UnalignedLoad<TagType>(ptr) == expected_tag);
509 field.AddNAlreadyReserved(idx - 1);
510 return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
511 }
512
FastF32R1(PROTOBUF_TC_PARAM_DECL)513 const char* TcParser::FastF32R1(PROTOBUF_TC_PARAM_DECL) {
514 PROTOBUF_MUSTTAIL return RepeatedFixed<uint32_t, uint8_t>(
515 PROTOBUF_TC_PARAM_PASS);
516 }
FastF32R2(PROTOBUF_TC_PARAM_DECL)517 const char* TcParser::FastF32R2(PROTOBUF_TC_PARAM_DECL) {
518 PROTOBUF_MUSTTAIL return RepeatedFixed<uint32_t, uint16_t>(
519 PROTOBUF_TC_PARAM_PASS);
520 }
FastF64R1(PROTOBUF_TC_PARAM_DECL)521 const char* TcParser::FastF64R1(PROTOBUF_TC_PARAM_DECL) {
522 PROTOBUF_MUSTTAIL return RepeatedFixed<uint64_t, uint8_t>(
523 PROTOBUF_TC_PARAM_PASS);
524 }
FastF64R2(PROTOBUF_TC_PARAM_DECL)525 const char* TcParser::FastF64R2(PROTOBUF_TC_PARAM_DECL) {
526 PROTOBUF_MUSTTAIL return RepeatedFixed<uint64_t, uint16_t>(
527 PROTOBUF_TC_PARAM_PASS);
528 }
529
530 // Note: some versions of GCC will fail with error "function not inlinable" if
531 // corecursive functions are both marked with PROTOBUF_ALWAYS_INLINE (Clang
532 // accepts this). We can still apply the attribute to one of the two functions,
533 // just not both (so we do mark the Repeated variant as always inlined). This
534 // also applies to PackedVarint, below.
535 template <typename LayoutType, typename TagType>
PackedFixed(PROTOBUF_TC_PARAM_DECL)536 const char* TcParser::PackedFixed(PROTOBUF_TC_PARAM_DECL) {
537 if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
538 // Try parsing as non-packed repeated:
539 constexpr WireFormatLite::WireType fallback_wt =
540 sizeof(LayoutType) == 4 ? WireFormatLite::WIRETYPE_FIXED32
541 : WireFormatLite::WIRETYPE_FIXED64;
542 InvertPacked<fallback_wt>(data);
543 if (data.coded_tag<TagType>() == 0) {
544 return RepeatedFixed<LayoutType, TagType>(PROTOBUF_TC_PARAM_PASS);
545 } else {
546 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
547 }
548 }
549 ptr += sizeof(TagType);
550 // Since ctx->ReadPackedFixed does not use TailCall<> or Return<>, sync any
551 // pending hasbits now:
552 SyncHasbits(msg, hasbits, table);
553 auto& field = RefAt<RepeatedField<LayoutType>>(msg, data.offset());
554 int size = ReadSize(&ptr);
555 // TODO(dlj): add a tailcalling variant of ReadPackedFixed.
556 return ctx->ReadPackedFixed(ptr, size,
557 static_cast<RepeatedField<LayoutType>*>(&field));
558 }
559
FastF32P1(PROTOBUF_TC_PARAM_DECL)560 const char* TcParser::FastF32P1(PROTOBUF_TC_PARAM_DECL) {
561 PROTOBUF_MUSTTAIL return PackedFixed<uint32_t, uint8_t>(
562 PROTOBUF_TC_PARAM_PASS);
563 }
FastF32P2(PROTOBUF_TC_PARAM_DECL)564 const char* TcParser::FastF32P2(PROTOBUF_TC_PARAM_DECL) {
565 PROTOBUF_MUSTTAIL return PackedFixed<uint32_t, uint16_t>(
566 PROTOBUF_TC_PARAM_PASS);
567 }
FastF64P1(PROTOBUF_TC_PARAM_DECL)568 const char* TcParser::FastF64P1(PROTOBUF_TC_PARAM_DECL) {
569 PROTOBUF_MUSTTAIL return PackedFixed<uint64_t, uint8_t>(
570 PROTOBUF_TC_PARAM_PASS);
571 }
FastF64P2(PROTOBUF_TC_PARAM_DECL)572 const char* TcParser::FastF64P2(PROTOBUF_TC_PARAM_DECL) {
573 PROTOBUF_MUSTTAIL return PackedFixed<uint64_t, uint16_t>(
574 PROTOBUF_TC_PARAM_PASS);
575 }
576
577 //////////////////////////////////////////////////////////////////////////////
578 // Varint fields
579 //////////////////////////////////////////////////////////////////////////////
580
581 namespace {
582
583 // Shift "byte" left by n * 7 bits, filling vacated bits with ones.
584 template <int n>
585 inline PROTOBUF_ALWAYS_INLINE uint64_t
shift_left_fill_with_ones(uint64_t byte,uint64_t ones)586 shift_left_fill_with_ones(uint64_t byte, uint64_t ones) {
587 return (byte << (n * 7)) | (ones >> (64 - (n * 7)));
588 }
589
590 // Shift "byte" left by n * 7 bits, filling vacated bits with ones, and
591 // put the new value in res. Return whether the result was negative.
592 template <int n>
shift_left_fill_with_ones_was_negative(uint64_t byte,uint64_t ones,int64_t & res)593 inline PROTOBUF_ALWAYS_INLINE bool shift_left_fill_with_ones_was_negative(
594 uint64_t byte, uint64_t ones, int64_t& res) {
595 #if defined(__GCC_ASM_FLAG_OUTPUTS__) && defined(__x86_64__)
596 // For the first two rounds (ptr[1] and ptr[2]), micro benchmarks show a
597 // substantial improvement from capturing the sign from the condition code
598 // register on x86-64.
599 bool sign_bit;
600 asm("shldq %3, %2, %1"
601 : "=@ccs"(sign_bit), "+r"(byte)
602 : "r"(ones), "i"(n * 7));
603 res = byte;
604 return sign_bit;
605 #else
606 // Generic fallback:
607 res = (byte << (n * 7)) | (ones >> (64 - (n * 7)));
608 return static_cast<int64_t>(res) < 0;
609 #endif
610 }
611
612 inline PROTOBUF_ALWAYS_INLINE std::pair<const char*, uint64_t>
Parse64FallbackPair(const char * p,int64_t res1)613 Parse64FallbackPair(const char* p, int64_t res1) {
614 auto ptr = reinterpret_cast<const int8_t*>(p);
615
616 // The algorithm relies on sign extension for each byte to set all high bits
617 // when the varint continues. It also relies on asserting all of the lower
618 // bits for each successive byte read. This allows the result to be aggregated
619 // using a bitwise AND. For example:
620 //
621 // 8 1 64 57 ... 24 17 16 9 8 1
622 // ptr[0] = 1aaa aaaa ; res1 = 1111 1111 ... 1111 1111 1111 1111 1aaa aaaa
623 // ptr[1] = 1bbb bbbb ; res2 = 1111 1111 ... 1111 1111 11bb bbbb b111 1111
624 // ptr[2] = 1ccc cccc ; res3 = 0000 0000 ... 000c cccc cc11 1111 1111 1111
625 // ---------------------------------------------
626 // res1 & res2 & res3 = 0000 0000 ... 000c cccc ccbb bbbb baaa aaaa
627 //
628 // On x86-64, a shld from a single register filled with enough 1s in the high
629 // bits can accomplish all this in one instruction. It so happens that res1
630 // has 57 high bits of ones, which is enough for the largest shift done.
631 GOOGLE_DCHECK_EQ(res1 >> 7, -1);
632 uint64_t ones = res1; // save the high 1 bits from res1 (input to SHLD)
633 int64_t res2, res3; // accumulated result chunks
634
635 if (!shift_left_fill_with_ones_was_negative<1>(ptr[1], ones, res2))
636 goto done2;
637 if (!shift_left_fill_with_ones_was_negative<2>(ptr[2], ones, res3))
638 goto done3;
639
640 // For the remainder of the chunks, check the sign of the AND result.
641 res1 &= shift_left_fill_with_ones<3>(ptr[3], ones);
642 if (res1 >= 0) goto done4;
643 res2 &= shift_left_fill_with_ones<4>(ptr[4], ones);
644 if (res2 >= 0) goto done5;
645 res3 &= shift_left_fill_with_ones<5>(ptr[5], ones);
646 if (res3 >= 0) goto done6;
647 res1 &= shift_left_fill_with_ones<6>(ptr[6], ones);
648 if (res1 >= 0) goto done7;
649 res2 &= shift_left_fill_with_ones<7>(ptr[7], ones);
650 if (res2 >= 0) goto done8;
651 res3 &= shift_left_fill_with_ones<8>(ptr[8], ones);
652 if (res3 >= 0) goto done9;
653
654 // For valid 64bit varints, the 10th byte/ptr[9] should be exactly 1. In this
655 // case, the continuation bit of ptr[8] already set the top bit of res3
656 // correctly, so all we have to do is check that the expected case is true.
657 if (PROTOBUF_PREDICT_TRUE(ptr[9] == 1)) goto done10;
658
659 // A value of 0, however, represents an over-serialized varint. This case
660 // should not happen, but if does (say, due to a nonconforming serializer),
661 // deassert the continuation bit that came from ptr[8].
662 if (ptr[9] == 0) {
663 #if defined(__GCC_ASM_FLAG_OUTPUTS__) && defined(__x86_64__)
664 // Use a small instruction since this is an uncommon code path.
665 asm("btcq $63,%0" : "+r"(res3));
666 #else
667 res3 ^= static_cast<uint64_t>(1) << 63;
668 #endif
669 goto done10;
670 }
671
672 // If the 10th byte/ptr[9] itself has any other value, then it is too big to
673 // fit in 64 bits. If the continue bit is set, it is an unterminated varint.
674 return {nullptr, 0};
675
676 done2:
677 return {p + 2, res1 & res2};
678 done3:
679 return {p + 3, res1 & res2 & res3};
680 done4:
681 return {p + 4, res1 & res2 & res3};
682 done5:
683 return {p + 5, res1 & res2 & res3};
684 done6:
685 return {p + 6, res1 & res2 & res3};
686 done7:
687 return {p + 7, res1 & res2 & res3};
688 done8:
689 return {p + 8, res1 & res2 & res3};
690 done9:
691 return {p + 9, res1 & res2 & res3};
692 done10:
693 return {p + 10, res1 & res2 & res3};
694 }
695
ParseVarint(const char * p,uint64_t * value)696 inline PROTOBUF_ALWAYS_INLINE const char* ParseVarint(const char* p,
697 uint64_t* value) {
698 int64_t byte = static_cast<int8_t>(*p);
699 if (PROTOBUF_PREDICT_TRUE(byte >= 0)) {
700 *value = byte;
701 return p + 1;
702 } else {
703 auto tmp = Parse64FallbackPair(p, byte);
704 if (PROTOBUF_PREDICT_TRUE(tmp.first)) *value = tmp.second;
705 return tmp.first;
706 }
707 }
708
709 template <typename FieldType, bool zigzag = false>
ZigZagDecodeHelper(uint64_t value)710 inline FieldType ZigZagDecodeHelper(uint64_t value) {
711 return static_cast<FieldType>(value);
712 }
713
714 template <>
ZigZagDecodeHelper(uint64_t value)715 inline int32_t ZigZagDecodeHelper<int32_t, true>(uint64_t value) {
716 return WireFormatLite::ZigZagDecode32(value);
717 }
718
719 template <>
ZigZagDecodeHelper(uint64_t value)720 inline int64_t ZigZagDecodeHelper<int64_t, true>(uint64_t value) {
721 return WireFormatLite::ZigZagDecode64(value);
722 }
723
EnumIsValidAux(int32_t val,uint16_t xform_val,TcParseTableBase::FieldAux aux)724 bool EnumIsValidAux(int32_t val, uint16_t xform_val,
725 TcParseTableBase::FieldAux aux) {
726 if (xform_val == field_layout::kTvRange) {
727 auto lo = aux.enum_range.start;
728 return lo <= val && val < (lo + aux.enum_range.length);
729 }
730 return aux.enum_validator(val);
731 }
732
733 } // namespace
734
735 template <typename FieldType, typename TagType, bool zigzag>
SingularVarint(PROTOBUF_TC_PARAM_DECL)736 PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularVarint(
737 PROTOBUF_TC_PARAM_DECL) {
738 if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
739 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
740 }
741 ptr += sizeof(TagType); // Consume tag
742 hasbits |= (uint64_t{1} << data.hasbit_idx());
743
744 // clang isn't smart enough to be able to only conditionally save
745 // registers to the stack, so we turn the integer-greater-than-128
746 // case into a separate routine.
747 if (PROTOBUF_PREDICT_FALSE(static_cast<int8_t>(*ptr) < 0)) {
748 PROTOBUF_MUSTTAIL return SingularVarBigint<FieldType, TagType, zigzag>(
749 PROTOBUF_TC_PARAM_PASS);
750 }
751
752 RefAt<FieldType>(msg, data.offset()) =
753 ZigZagDecodeHelper<FieldType, zigzag>(static_cast<uint8_t>(*ptr++));
754 PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
755 }
756
757 template <typename FieldType, typename TagType, bool zigzag>
SingularVarBigint(PROTOBUF_TC_PARAM_DECL)758 PROTOBUF_NOINLINE const char* TcParser::SingularVarBigint(
759 PROTOBUF_TC_PARAM_DECL) {
760 // For some reason clang wants to save 5 registers to the stack here,
761 // but we only need four for this code, so save the data we don't need
762 // to the stack. Happily, saving them this way uses regular store
763 // instructions rather than PUSH/POP, which saves time at the cost of greater
764 // code size, but for this heavily-used piece of code, that's fine.
765 struct Spill {
766 uint64_t field_data;
767 ::google::protobuf::MessageLite* msg;
768 const ::google::protobuf::internal::TcParseTableBase* table;
769 uint64_t hasbits;
770 };
771 volatile Spill spill = {data.data, msg, table, hasbits};
772
773 uint64_t tmp;
774 PROTOBUF_ASSUME(static_cast<int8_t>(*ptr) < 0);
775 ptr = ParseVarint(ptr, &tmp);
776
777 data.data = spill.field_data;
778 msg = spill.msg;
779 table = spill.table;
780 hasbits = spill.hasbits;
781
782 if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
783 return Error(PROTOBUF_TC_PARAM_PASS);
784 }
785 RefAt<FieldType>(msg, data.offset()) =
786 ZigZagDecodeHelper<FieldType, zigzag>(tmp);
787 PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
788 }
789
FastV8S1(PROTOBUF_TC_PARAM_DECL)790 const char* TcParser::FastV8S1(PROTOBUF_TC_PARAM_DECL) {
791 PROTOBUF_MUSTTAIL return SingularVarint<bool, uint8_t>(
792 PROTOBUF_TC_PARAM_PASS);
793 }
FastV8S2(PROTOBUF_TC_PARAM_DECL)794 const char* TcParser::FastV8S2(PROTOBUF_TC_PARAM_DECL) {
795 PROTOBUF_MUSTTAIL return SingularVarint<bool, uint16_t>(
796 PROTOBUF_TC_PARAM_PASS);
797 }
FastV32S1(PROTOBUF_TC_PARAM_DECL)798 const char* TcParser::FastV32S1(PROTOBUF_TC_PARAM_DECL) {
799 PROTOBUF_MUSTTAIL return SingularVarint<uint32_t, uint8_t>(
800 PROTOBUF_TC_PARAM_PASS);
801 }
FastV32S2(PROTOBUF_TC_PARAM_DECL)802 const char* TcParser::FastV32S2(PROTOBUF_TC_PARAM_DECL) {
803 PROTOBUF_MUSTTAIL return SingularVarint<uint32_t, uint16_t>(
804 PROTOBUF_TC_PARAM_PASS);
805 }
FastV64S1(PROTOBUF_TC_PARAM_DECL)806 const char* TcParser::FastV64S1(PROTOBUF_TC_PARAM_DECL) {
807 PROTOBUF_MUSTTAIL return SingularVarint<uint64_t, uint8_t>(
808 PROTOBUF_TC_PARAM_PASS);
809 }
FastV64S2(PROTOBUF_TC_PARAM_DECL)810 const char* TcParser::FastV64S2(PROTOBUF_TC_PARAM_DECL) {
811 PROTOBUF_MUSTTAIL return SingularVarint<uint64_t, uint16_t>(
812 PROTOBUF_TC_PARAM_PASS);
813 }
814
FastZ32S1(PROTOBUF_TC_PARAM_DECL)815 const char* TcParser::FastZ32S1(PROTOBUF_TC_PARAM_DECL) {
816 PROTOBUF_MUSTTAIL return SingularVarint<int32_t, uint8_t, true>(
817 PROTOBUF_TC_PARAM_PASS);
818 }
FastZ32S2(PROTOBUF_TC_PARAM_DECL)819 const char* TcParser::FastZ32S2(PROTOBUF_TC_PARAM_DECL) {
820 PROTOBUF_MUSTTAIL return SingularVarint<int32_t, uint16_t, true>(
821 PROTOBUF_TC_PARAM_PASS);
822 }
FastZ64S1(PROTOBUF_TC_PARAM_DECL)823 const char* TcParser::FastZ64S1(PROTOBUF_TC_PARAM_DECL) {
824 PROTOBUF_MUSTTAIL return SingularVarint<int64_t, uint8_t, true>(
825 PROTOBUF_TC_PARAM_PASS);
826 }
FastZ64S2(PROTOBUF_TC_PARAM_DECL)827 const char* TcParser::FastZ64S2(PROTOBUF_TC_PARAM_DECL) {
828 PROTOBUF_MUSTTAIL return SingularVarint<int64_t, uint16_t, true>(
829 PROTOBUF_TC_PARAM_PASS);
830 }
831
832 template <typename FieldType, typename TagType, bool zigzag>
RepeatedVarint(PROTOBUF_TC_PARAM_DECL)833 PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedVarint(
834 PROTOBUF_TC_PARAM_DECL) {
835 if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
836 // Try parsing as non-packed repeated:
837 InvertPacked<WireFormatLite::WIRETYPE_VARINT>(data);
838 if (data.coded_tag<TagType>() == 0) {
839 return PackedVarint<FieldType, TagType, zigzag>(PROTOBUF_TC_PARAM_PASS);
840 } else {
841 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
842 }
843 }
844 auto& field = RefAt<RepeatedField<FieldType>>(msg, data.offset());
845 auto expected_tag = UnalignedLoad<TagType>(ptr);
846 do {
847 ptr += sizeof(TagType);
848 uint64_t tmp;
849 ptr = ParseVarint(ptr, &tmp);
850 if (ptr == nullptr) {
851 return Error(PROTOBUF_TC_PARAM_PASS);
852 }
853 field.Add(ZigZagDecodeHelper<FieldType, zigzag>(tmp));
854 if (!ctx->DataAvailable(ptr)) {
855 break;
856 }
857 } while (UnalignedLoad<TagType>(ptr) == expected_tag);
858 return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
859 }
860
FastV8R1(PROTOBUF_TC_PARAM_DECL)861 const char* TcParser::FastV8R1(PROTOBUF_TC_PARAM_DECL) {
862 PROTOBUF_MUSTTAIL return RepeatedVarint<bool, uint8_t>(
863 PROTOBUF_TC_PARAM_PASS);
864 }
FastV8R2(PROTOBUF_TC_PARAM_DECL)865 const char* TcParser::FastV8R2(PROTOBUF_TC_PARAM_DECL) {
866 PROTOBUF_MUSTTAIL return RepeatedVarint<bool, uint16_t>(
867 PROTOBUF_TC_PARAM_PASS);
868 }
FastV32R1(PROTOBUF_TC_PARAM_DECL)869 const char* TcParser::FastV32R1(PROTOBUF_TC_PARAM_DECL) {
870 PROTOBUF_MUSTTAIL return RepeatedVarint<uint32_t, uint8_t>(
871 PROTOBUF_TC_PARAM_PASS);
872 }
FastV32R2(PROTOBUF_TC_PARAM_DECL)873 const char* TcParser::FastV32R2(PROTOBUF_TC_PARAM_DECL) {
874 PROTOBUF_MUSTTAIL return RepeatedVarint<uint32_t, uint16_t>(
875 PROTOBUF_TC_PARAM_PASS);
876 }
FastV64R1(PROTOBUF_TC_PARAM_DECL)877 const char* TcParser::FastV64R1(PROTOBUF_TC_PARAM_DECL) {
878 PROTOBUF_MUSTTAIL return RepeatedVarint<uint64_t, uint8_t>(
879 PROTOBUF_TC_PARAM_PASS);
880 }
FastV64R2(PROTOBUF_TC_PARAM_DECL)881 const char* TcParser::FastV64R2(PROTOBUF_TC_PARAM_DECL) {
882 PROTOBUF_MUSTTAIL return RepeatedVarint<uint64_t, uint16_t>(
883 PROTOBUF_TC_PARAM_PASS);
884 }
885
FastZ32R1(PROTOBUF_TC_PARAM_DECL)886 const char* TcParser::FastZ32R1(PROTOBUF_TC_PARAM_DECL) {
887 PROTOBUF_MUSTTAIL return RepeatedVarint<int32_t, uint8_t, true>(
888 PROTOBUF_TC_PARAM_PASS);
889 }
FastZ32R2(PROTOBUF_TC_PARAM_DECL)890 const char* TcParser::FastZ32R2(PROTOBUF_TC_PARAM_DECL) {
891 PROTOBUF_MUSTTAIL return RepeatedVarint<int32_t, uint16_t, true>(
892 PROTOBUF_TC_PARAM_PASS);
893 }
FastZ64R1(PROTOBUF_TC_PARAM_DECL)894 const char* TcParser::FastZ64R1(PROTOBUF_TC_PARAM_DECL) {
895 PROTOBUF_MUSTTAIL return RepeatedVarint<int64_t, uint8_t, true>(
896 PROTOBUF_TC_PARAM_PASS);
897 }
FastZ64R2(PROTOBUF_TC_PARAM_DECL)898 const char* TcParser::FastZ64R2(PROTOBUF_TC_PARAM_DECL) {
899 PROTOBUF_MUSTTAIL return RepeatedVarint<int64_t, uint16_t, true>(
900 PROTOBUF_TC_PARAM_PASS);
901 }
902
903 // See comment on PackedFixed for why this is not PROTOBUF_ALWAYS_INLINE.
904 template <typename FieldType, typename TagType, bool zigzag>
PackedVarint(PROTOBUF_TC_PARAM_DECL)905 const char* TcParser::PackedVarint(PROTOBUF_TC_PARAM_DECL) {
906 if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
907 InvertPacked<WireFormatLite::WIRETYPE_VARINT>(data);
908 if (data.coded_tag<TagType>() == 0) {
909 return RepeatedVarint<FieldType, TagType, zigzag>(PROTOBUF_TC_PARAM_PASS);
910 } else {
911 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
912 }
913 }
914 ptr += sizeof(TagType);
915 // Since ctx->ReadPackedVarint does not use TailCall or Return, sync any
916 // pending hasbits now:
917 SyncHasbits(msg, hasbits, table);
918 auto* field = &RefAt<RepeatedField<FieldType>>(msg, data.offset());
919 return ctx->ReadPackedVarint(ptr, [field](uint64_t varint) {
920 FieldType val;
921 if (zigzag) {
922 if (sizeof(FieldType) == 8) {
923 val = WireFormatLite::ZigZagDecode64(varint);
924 } else {
925 val = WireFormatLite::ZigZagDecode32(varint);
926 }
927 } else {
928 val = varint;
929 }
930 field->Add(val);
931 });
932 }
933
FastV8P1(PROTOBUF_TC_PARAM_DECL)934 const char* TcParser::FastV8P1(PROTOBUF_TC_PARAM_DECL) {
935 PROTOBUF_MUSTTAIL return PackedVarint<bool, uint8_t>(PROTOBUF_TC_PARAM_PASS);
936 }
FastV8P2(PROTOBUF_TC_PARAM_DECL)937 const char* TcParser::FastV8P2(PROTOBUF_TC_PARAM_DECL) {
938 PROTOBUF_MUSTTAIL return PackedVarint<bool, uint16_t>(PROTOBUF_TC_PARAM_PASS);
939 }
FastV32P1(PROTOBUF_TC_PARAM_DECL)940 const char* TcParser::FastV32P1(PROTOBUF_TC_PARAM_DECL) {
941 PROTOBUF_MUSTTAIL return PackedVarint<uint32_t, uint8_t>(
942 PROTOBUF_TC_PARAM_PASS);
943 }
FastV32P2(PROTOBUF_TC_PARAM_DECL)944 const char* TcParser::FastV32P2(PROTOBUF_TC_PARAM_DECL) {
945 PROTOBUF_MUSTTAIL return PackedVarint<uint32_t, uint16_t>(
946 PROTOBUF_TC_PARAM_PASS);
947 }
FastV64P1(PROTOBUF_TC_PARAM_DECL)948 const char* TcParser::FastV64P1(PROTOBUF_TC_PARAM_DECL) {
949 PROTOBUF_MUSTTAIL return PackedVarint<uint64_t, uint8_t>(
950 PROTOBUF_TC_PARAM_PASS);
951 }
FastV64P2(PROTOBUF_TC_PARAM_DECL)952 const char* TcParser::FastV64P2(PROTOBUF_TC_PARAM_DECL) {
953 PROTOBUF_MUSTTAIL return PackedVarint<uint64_t, uint16_t>(
954 PROTOBUF_TC_PARAM_PASS);
955 }
956
FastZ32P1(PROTOBUF_TC_PARAM_DECL)957 const char* TcParser::FastZ32P1(PROTOBUF_TC_PARAM_DECL) {
958 PROTOBUF_MUSTTAIL return PackedVarint<int32_t, uint8_t, true>(
959 PROTOBUF_TC_PARAM_PASS);
960 }
FastZ32P2(PROTOBUF_TC_PARAM_DECL)961 const char* TcParser::FastZ32P2(PROTOBUF_TC_PARAM_DECL) {
962 PROTOBUF_MUSTTAIL return PackedVarint<int32_t, uint16_t, true>(
963 PROTOBUF_TC_PARAM_PASS);
964 }
FastZ64P1(PROTOBUF_TC_PARAM_DECL)965 const char* TcParser::FastZ64P1(PROTOBUF_TC_PARAM_DECL) {
966 PROTOBUF_MUSTTAIL return PackedVarint<int64_t, uint8_t, true>(
967 PROTOBUF_TC_PARAM_PASS);
968 }
FastZ64P2(PROTOBUF_TC_PARAM_DECL)969 const char* TcParser::FastZ64P2(PROTOBUF_TC_PARAM_DECL) {
970 PROTOBUF_MUSTTAIL return PackedVarint<int64_t, uint16_t, true>(
971 PROTOBUF_TC_PARAM_PASS);
972 }
973
974 //////////////////////////////////////////////////////////////////////////////
975 // Enum fields
976 //////////////////////////////////////////////////////////////////////////////
977
FastUnknownEnumFallback(PROTOBUF_TC_PARAM_DECL)978 PROTOBUF_NOINLINE const char* TcParser::FastUnknownEnumFallback(
979 PROTOBUF_TC_PARAM_DECL) {
980 (void)msg;
981 (void)ctx;
982 (void)hasbits;
983
984 // If we know we want to put this field directly into the unknown field set,
985 // then we can skip the call to MiniParse and directly call table->fallback.
986 // However, we first have to update `data` to contain the decoded tag.
987 uint32_t tag;
988 ptr = ReadTag(ptr, &tag);
989 if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
990 return Error(PROTOBUF_TC_PARAM_PASS);
991 }
992 data.data = tag;
993 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
994 }
995
996 template <typename TagType, uint16_t xform_val>
SingularEnum(PROTOBUF_TC_PARAM_DECL)997 PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularEnum(
998 PROTOBUF_TC_PARAM_DECL) {
999 if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
1000 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
1001 }
1002 const char* ptr2 = ptr; // Save for unknown enum case
1003 ptr += sizeof(TagType); // Consume tag
1004 uint64_t tmp;
1005 ptr = ParseVarint(ptr, &tmp);
1006 if (ptr == nullptr) {
1007 return Error(PROTOBUF_TC_PARAM_PASS);
1008 }
1009 const TcParseTableBase::FieldAux aux = *table->field_aux(data.aux_idx());
1010 if (PROTOBUF_PREDICT_FALSE(
1011 !EnumIsValidAux(static_cast<int32_t>(tmp), xform_val, aux))) {
1012 ptr = ptr2;
1013 PROTOBUF_MUSTTAIL return FastUnknownEnumFallback(PROTOBUF_TC_PARAM_PASS);
1014 }
1015 hasbits |= (uint64_t{1} << data.hasbit_idx());
1016 RefAt<int32_t>(msg, data.offset()) = tmp;
1017 PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
1018 }
1019
FastErS1(PROTOBUF_TC_PARAM_DECL)1020 const char* TcParser::FastErS1(PROTOBUF_TC_PARAM_DECL) {
1021 PROTOBUF_MUSTTAIL return SingularEnum<uint8_t, field_layout::kTvRange>(
1022 PROTOBUF_TC_PARAM_PASS);
1023 }
FastErS2(PROTOBUF_TC_PARAM_DECL)1024 const char* TcParser::FastErS2(PROTOBUF_TC_PARAM_DECL) {
1025 PROTOBUF_MUSTTAIL return SingularEnum<uint16_t, field_layout::kTvRange>(
1026 PROTOBUF_TC_PARAM_PASS);
1027 }
FastEvS1(PROTOBUF_TC_PARAM_DECL)1028 const char* TcParser::FastEvS1(PROTOBUF_TC_PARAM_DECL) {
1029 PROTOBUF_MUSTTAIL return SingularEnum<uint8_t, field_layout::kTvEnum>(
1030 PROTOBUF_TC_PARAM_PASS);
1031 }
FastEvS2(PROTOBUF_TC_PARAM_DECL)1032 const char* TcParser::FastEvS2(PROTOBUF_TC_PARAM_DECL) {
1033 PROTOBUF_MUSTTAIL return SingularEnum<uint16_t, field_layout::kTvEnum>(
1034 PROTOBUF_TC_PARAM_PASS);
1035 }
1036
1037 template <typename TagType, uint16_t xform_val>
RepeatedEnum(PROTOBUF_TC_PARAM_DECL)1038 PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedEnum(
1039 PROTOBUF_TC_PARAM_DECL) {
1040 if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
1041 InvertPacked<WireFormatLite::WIRETYPE_VARINT>(data);
1042 if (data.coded_tag<TagType>() == 0) {
1043 // Packed parsing is handled by generated fallback.
1044 PROTOBUF_MUSTTAIL return FastUnknownEnumFallback(PROTOBUF_TC_PARAM_PASS);
1045 } else {
1046 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
1047 }
1048 }
1049 auto& field = RefAt<RepeatedField<int32_t>>(msg, data.offset());
1050 auto expected_tag = UnalignedLoad<TagType>(ptr);
1051 const TcParseTableBase::FieldAux aux = *table->field_aux(data.aux_idx());
1052 do {
1053 const char* ptr2 = ptr; // save for unknown enum case
1054 ptr += sizeof(TagType);
1055 uint64_t tmp;
1056 ptr = ParseVarint(ptr, &tmp);
1057 if (ptr == nullptr) {
1058 return Error(PROTOBUF_TC_PARAM_PASS);
1059 }
1060 if (PROTOBUF_PREDICT_FALSE(
1061 !EnumIsValidAux(static_cast<int32_t>(tmp), xform_val, aux))) {
1062 // We can avoid duplicate work in MiniParse by directly calling
1063 // table->fallback.
1064 ptr = ptr2;
1065 PROTOBUF_MUSTTAIL return FastUnknownEnumFallback(PROTOBUF_TC_PARAM_PASS);
1066 }
1067 field.Add(static_cast<int32_t>(tmp));
1068 if (!ctx->DataAvailable(ptr)) {
1069 break;
1070 }
1071 } while (UnalignedLoad<TagType>(ptr) == expected_tag);
1072 return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
1073 }
1074
FastErR1(PROTOBUF_TC_PARAM_DECL)1075 const char* TcParser::FastErR1(PROTOBUF_TC_PARAM_DECL) {
1076 PROTOBUF_MUSTTAIL return RepeatedEnum<uint8_t, field_layout::kTvRange>(
1077 PROTOBUF_TC_PARAM_PASS);
1078 }
FastErR2(PROTOBUF_TC_PARAM_DECL)1079 const char* TcParser::FastErR2(PROTOBUF_TC_PARAM_DECL) {
1080 PROTOBUF_MUSTTAIL return RepeatedEnum<uint16_t, field_layout::kTvRange>(
1081 PROTOBUF_TC_PARAM_PASS);
1082 }
FastEvR1(PROTOBUF_TC_PARAM_DECL)1083 const char* TcParser::FastEvR1(PROTOBUF_TC_PARAM_DECL) {
1084 PROTOBUF_MUSTTAIL return RepeatedEnum<uint8_t, field_layout::kTvEnum>(
1085 PROTOBUF_TC_PARAM_PASS);
1086 }
FastEvR2(PROTOBUF_TC_PARAM_DECL)1087 const char* TcParser::FastEvR2(PROTOBUF_TC_PARAM_DECL) {
1088 PROTOBUF_MUSTTAIL return RepeatedEnum<uint16_t, field_layout::kTvEnum>(
1089 PROTOBUF_TC_PARAM_PASS);
1090 }
1091
1092 //////////////////////////////////////////////////////////////////////////////
1093 // String/bytes fields
1094 //////////////////////////////////////////////////////////////////////////////
1095
1096 // Defined in wire_format_lite.cc
1097 void PrintUTF8ErrorLog(StringPiece message_name,
1098 StringPiece field_name, const char* operation_str,
1099 bool emit_stacktrace);
1100
ReportFastUtf8Error(uint32_t decoded_tag,const TcParseTableBase * table)1101 void TcParser::ReportFastUtf8Error(uint32_t decoded_tag,
1102 const TcParseTableBase* table) {
1103 uint32_t field_num = decoded_tag >> 3;
1104 const auto* entry = FindFieldEntry(table, field_num);
1105 PrintUTF8ErrorLog(MessageName(table), FieldName(table, entry), "parsing",
1106 false);
1107 }
1108
1109 namespace {
1110
1111 PROTOBUF_NOINLINE
SingularStringParserFallback(ArenaStringPtr * s,const char * ptr,EpsCopyInputStream * stream)1112 const char* SingularStringParserFallback(ArenaStringPtr* s, const char* ptr,
1113 EpsCopyInputStream* stream) {
1114 int size = ReadSize(&ptr);
1115 if (!ptr) return nullptr;
1116 return stream->ReadString(ptr, size, s->MutableNoCopy(nullptr));
1117 }
1118
1119 } // namespace
1120
1121 template <typename TagType, TcParser::Utf8Type utf8>
SingularString(PROTOBUF_TC_PARAM_DECL)1122 PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularString(
1123 PROTOBUF_TC_PARAM_DECL) {
1124 if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
1125 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
1126 }
1127 auto saved_tag = UnalignedLoad<TagType>(ptr);
1128 ptr += sizeof(TagType);
1129 hasbits |= (uint64_t{1} << data.hasbit_idx());
1130 auto& field = RefAt<ArenaStringPtr>(msg, data.offset());
1131 auto arena = ctx->data().arena;
1132 if (arena) {
1133 ptr = ctx->ReadArenaString(ptr, &field, arena);
1134 } else {
1135 ptr = SingularStringParserFallback(&field, ptr, ctx);
1136 }
1137 if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
1138 switch (utf8) {
1139 case kNoUtf8:
1140 #ifdef NDEBUG
1141 case kUtf8ValidateOnly:
1142 #endif
1143 return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
1144 default:
1145 if (PROTOBUF_PREDICT_TRUE(IsStructurallyValidUTF8(field.Get()))) {
1146 return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
1147 }
1148 ReportFastUtf8Error(FastDecodeTag(saved_tag), table);
1149 return utf8 == kUtf8 ? Error(PROTOBUF_TC_PARAM_PASS)
1150 : ToParseLoop(PROTOBUF_TC_PARAM_PASS);
1151 }
1152 }
1153
FastBS1(PROTOBUF_TC_PARAM_DECL)1154 const char* TcParser::FastBS1(PROTOBUF_TC_PARAM_DECL) {
1155 PROTOBUF_MUSTTAIL return SingularString<uint8_t, kNoUtf8>(
1156 PROTOBUF_TC_PARAM_PASS);
1157 }
FastBS2(PROTOBUF_TC_PARAM_DECL)1158 const char* TcParser::FastBS2(PROTOBUF_TC_PARAM_DECL) {
1159 PROTOBUF_MUSTTAIL return SingularString<uint16_t, kNoUtf8>(
1160 PROTOBUF_TC_PARAM_PASS);
1161 }
FastSS1(PROTOBUF_TC_PARAM_DECL)1162 const char* TcParser::FastSS1(PROTOBUF_TC_PARAM_DECL) {
1163 PROTOBUF_MUSTTAIL return SingularString<uint8_t, kUtf8ValidateOnly>(
1164 PROTOBUF_TC_PARAM_PASS);
1165 }
FastSS2(PROTOBUF_TC_PARAM_DECL)1166 const char* TcParser::FastSS2(PROTOBUF_TC_PARAM_DECL) {
1167 PROTOBUF_MUSTTAIL return SingularString<uint16_t, kUtf8ValidateOnly>(
1168 PROTOBUF_TC_PARAM_PASS);
1169 }
FastUS1(PROTOBUF_TC_PARAM_DECL)1170 const char* TcParser::FastUS1(PROTOBUF_TC_PARAM_DECL) {
1171 PROTOBUF_MUSTTAIL return SingularString<uint8_t, kUtf8>(
1172 PROTOBUF_TC_PARAM_PASS);
1173 }
FastUS2(PROTOBUF_TC_PARAM_DECL)1174 const char* TcParser::FastUS2(PROTOBUF_TC_PARAM_DECL) {
1175 PROTOBUF_MUSTTAIL return SingularString<uint16_t, kUtf8>(
1176 PROTOBUF_TC_PARAM_PASS);
1177 }
1178
1179 // Inlined string variants:
1180
FastBiS1(PROTOBUF_TC_PARAM_DECL)1181 const char* TcParser::FastBiS1(PROTOBUF_TC_PARAM_DECL) {
1182 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
1183 }
FastBiS2(PROTOBUF_TC_PARAM_DECL)1184 const char* TcParser::FastBiS2(PROTOBUF_TC_PARAM_DECL) {
1185 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
1186 }
FastSiS1(PROTOBUF_TC_PARAM_DECL)1187 const char* TcParser::FastSiS1(PROTOBUF_TC_PARAM_DECL) {
1188 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
1189 }
FastSiS2(PROTOBUF_TC_PARAM_DECL)1190 const char* TcParser::FastSiS2(PROTOBUF_TC_PARAM_DECL) {
1191 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
1192 }
FastUiS1(PROTOBUF_TC_PARAM_DECL)1193 const char* TcParser::FastUiS1(PROTOBUF_TC_PARAM_DECL) {
1194 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
1195 }
FastUiS2(PROTOBUF_TC_PARAM_DECL)1196 const char* TcParser::FastUiS2(PROTOBUF_TC_PARAM_DECL) {
1197 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
1198 }
1199
1200 template <typename TagType, TcParser::Utf8Type utf8>
RepeatedString(PROTOBUF_TC_PARAM_DECL)1201 PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedString(
1202 PROTOBUF_TC_PARAM_DECL) {
1203 if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
1204 PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
1205 }
1206 auto expected_tag = UnalignedLoad<TagType>(ptr);
1207 auto& field = RefAt<RepeatedPtrField<std::string>>(msg, data.offset());
1208 do {
1209 ptr += sizeof(TagType);
1210 std::string* str = field.Add();
1211 ptr = InlineGreedyStringParser(str, ptr, ctx);
1212 if (ptr == nullptr) {
1213 return Error(PROTOBUF_TC_PARAM_PASS);
1214 }
1215 switch (utf8) {
1216 case kNoUtf8:
1217 #ifdef NDEBUG
1218 case kUtf8ValidateOnly:
1219 #endif
1220 break;
1221 default:
1222 if (PROTOBUF_PREDICT_TRUE(IsStructurallyValidUTF8(*str))) {
1223 break;
1224 }
1225 ReportFastUtf8Error(FastDecodeTag(expected_tag), table);
1226 if (utf8 == kUtf8) return Error(PROTOBUF_TC_PARAM_PASS);
1227 break;
1228 }
1229 if (!ctx->DataAvailable(ptr)) break;
1230 } while (UnalignedLoad<TagType>(ptr) == expected_tag);
1231 return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
1232 }
1233
FastBR1(PROTOBUF_TC_PARAM_DECL)1234 const char* TcParser::FastBR1(PROTOBUF_TC_PARAM_DECL) {
1235 PROTOBUF_MUSTTAIL return RepeatedString<uint8_t, kNoUtf8>(
1236 PROTOBUF_TC_PARAM_PASS);
1237 }
FastBR2(PROTOBUF_TC_PARAM_DECL)1238 const char* TcParser::FastBR2(PROTOBUF_TC_PARAM_DECL) {
1239 PROTOBUF_MUSTTAIL return RepeatedString<uint16_t, kNoUtf8>(
1240 PROTOBUF_TC_PARAM_PASS);
1241 }
FastSR1(PROTOBUF_TC_PARAM_DECL)1242 const char* TcParser::FastSR1(PROTOBUF_TC_PARAM_DECL) {
1243 PROTOBUF_MUSTTAIL return RepeatedString<uint8_t, kUtf8ValidateOnly>(
1244 PROTOBUF_TC_PARAM_PASS);
1245 }
FastSR2(PROTOBUF_TC_PARAM_DECL)1246 const char* TcParser::FastSR2(PROTOBUF_TC_PARAM_DECL) {
1247 PROTOBUF_MUSTTAIL return RepeatedString<uint16_t, kUtf8ValidateOnly>(
1248 PROTOBUF_TC_PARAM_PASS);
1249 }
FastUR1(PROTOBUF_TC_PARAM_DECL)1250 const char* TcParser::FastUR1(PROTOBUF_TC_PARAM_DECL) {
1251 PROTOBUF_MUSTTAIL return RepeatedString<uint8_t, kUtf8>(
1252 PROTOBUF_TC_PARAM_PASS);
1253 }
FastUR2(PROTOBUF_TC_PARAM_DECL)1254 const char* TcParser::FastUR2(PROTOBUF_TC_PARAM_DECL) {
1255 PROTOBUF_MUSTTAIL return RepeatedString<uint16_t, kUtf8>(
1256 PROTOBUF_TC_PARAM_PASS);
1257 }
1258
1259 //////////////////////////////////////////////////////////////////////////////
1260 // Mini parsing
1261 //////////////////////////////////////////////////////////////////////////////
1262
1263 namespace {
SetHas(const TcParseTableBase * table,const FieldEntry & entry,MessageLite * msg,uint64_t & hasbits)1264 inline void SetHas(const TcParseTableBase* table, const FieldEntry& entry,
1265 MessageLite* msg, uint64_t& hasbits) {
1266 int32_t has_idx = entry.has_idx;
1267 if (has_idx < 32) {
1268 hasbits |= uint64_t{1} << has_idx;
1269 } else {
1270 auto* hasblocks = &TcParser::RefAt<uint32_t>(msg, table->has_bits_offset);
1271 #if defined(__x86_64__) && defined(__GNUC__)
1272 asm("bts %1, %0\n" : "+m"(*hasblocks) : "r"(has_idx));
1273 #else
1274 auto& hasblock = hasblocks[has_idx / 32];
1275 hasblock |= uint32_t{1} << (has_idx % 32);
1276 #endif
1277 }
1278 }
1279 } // namespace
1280
1281 // Destroys any existing oneof union member (if necessary). Returns true if the
1282 // caller is responsible for initializing the object, or false if the field
1283 // already has the desired case.
ChangeOneof(const TcParseTableBase * table,const TcParseTableBase::FieldEntry & entry,uint32_t field_num,ParseContext * ctx,MessageLite * msg)1284 bool TcParser::ChangeOneof(const TcParseTableBase* table,
1285 const TcParseTableBase::FieldEntry& entry,
1286 uint32_t field_num, ParseContext* ctx,
1287 MessageLite* msg) {
1288 // The _oneof_case_ array offset is stored in the first aux entry.
1289 uint32_t oneof_case_offset = table->field_aux(0u)->offset;
1290 // The _oneof_case_ array index is stored in the has-bit index.
1291 uint32_t* oneof_case =
1292 &TcParser::RefAt<uint32_t>(msg, oneof_case_offset) + entry.has_idx;
1293 uint32_t current_case = *oneof_case;
1294 *oneof_case = field_num;
1295
1296 if (current_case == 0) {
1297 // If the member is empty, we don't have anything to clear. Caller is
1298 // responsible for creating a new member object.
1299 return true;
1300 }
1301 if (current_case == field_num) {
1302 // If the member is already active, then it should be merged. We're done.
1303 return false;
1304 }
1305 // Look up the value that is already stored, and dispose of it if necessary.
1306 const FieldEntry* current_entry = FindFieldEntry(table, current_case);
1307 uint16_t current_kind = current_entry->type_card & field_layout::kFkMask;
1308 uint16_t current_rep = current_entry->type_card & field_layout::kRepMask;
1309 if (current_kind == field_layout::kFkString) {
1310 switch (current_rep) {
1311 case field_layout::kRepAString: {
1312 auto& field = RefAt<ArenaStringPtr>(msg, current_entry->offset);
1313 field.Destroy();
1314 break;
1315 }
1316 case field_layout::kRepSString:
1317 case field_layout::kRepIString:
1318 default:
1319 GOOGLE_LOG(DFATAL) << "string rep not handled: "
1320 << (current_rep >> field_layout::kRepShift);
1321 return true;
1322 }
1323 } else if (current_kind == field_layout::kFkMessage) {
1324 switch (current_rep) {
1325 case field_layout::kRepMessage:
1326 case field_layout::kRepGroup:
1327 case field_layout::kRepIWeak: {
1328 auto& field = RefAt<MessageLite*>(msg, current_entry->offset);
1329 if (!ctx->data().arena) {
1330 delete field;
1331 }
1332 break;
1333 }
1334 default:
1335 GOOGLE_LOG(DFATAL) << "message rep not handled: "
1336 << (current_rep >> field_layout::kRepShift);
1337 break;
1338 }
1339 }
1340 return true;
1341 }
1342
MpFixed(PROTOBUF_TC_PARAM_DECL)1343 const char* TcParser::MpFixed(PROTOBUF_TC_PARAM_DECL) {
1344 const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
1345 const uint16_t type_card = entry.type_card;
1346 const uint16_t card = type_card & field_layout::kFcMask;
1347
1348 // Check for repeated parsing (wiretype fallback is handled there):
1349 if (card == field_layout::kFcRepeated) {
1350 PROTOBUF_MUSTTAIL return MpRepeatedFixed(PROTOBUF_TC_PARAM_PASS);
1351 }
1352 // Check for mismatched wiretype:
1353 const uint16_t rep = type_card & field_layout::kRepMask;
1354 const uint32_t decoded_wiretype = data.tag() & 7;
1355 if (rep == field_layout::kRep64Bits) {
1356 if (decoded_wiretype != WireFormatLite::WIRETYPE_FIXED64) {
1357 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1358 }
1359 } else {
1360 GOOGLE_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep32Bits));
1361 if (decoded_wiretype != WireFormatLite::WIRETYPE_FIXED32) {
1362 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1363 }
1364 }
1365 // Set the field present:
1366 if (card == field_layout::kFcOptional) {
1367 SetHas(table, entry, msg, hasbits);
1368 } else if (card == field_layout::kFcOneof) {
1369 ChangeOneof(table, entry, data.tag() >> 3, ctx, msg);
1370 }
1371 // Copy the value:
1372 if (rep == field_layout::kRep64Bits) {
1373 RefAt<uint64_t>(msg, entry.offset) = UnalignedLoad<uint64_t>(ptr);
1374 ptr += sizeof(uint64_t);
1375 } else {
1376 RefAt<uint32_t>(msg, entry.offset) = UnalignedLoad<uint32_t>(ptr);
1377 ptr += sizeof(uint32_t);
1378 }
1379 PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
1380 }
1381
MpRepeatedFixed(PROTOBUF_TC_PARAM_DECL)1382 const char* TcParser::MpRepeatedFixed(PROTOBUF_TC_PARAM_DECL) {
1383 const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
1384 const uint32_t decoded_tag = data.tag();
1385 const uint32_t decoded_wiretype = decoded_tag & 7;
1386
1387 // Check for packed repeated fallback:
1388 if (decoded_wiretype == WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
1389 PROTOBUF_MUSTTAIL return MpPackedFixed(PROTOBUF_TC_PARAM_PASS);
1390 }
1391
1392 const uint16_t type_card = entry.type_card;
1393 const uint16_t rep = type_card & field_layout::kRepMask;
1394 if (rep == field_layout::kRep64Bits) {
1395 if (decoded_wiretype != WireFormatLite::WIRETYPE_FIXED64) {
1396 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1397 }
1398 auto& field = RefAt<RepeatedField<uint64_t>>(msg, entry.offset);
1399 constexpr auto size = sizeof(uint64_t);
1400 const char* ptr2 = ptr;
1401 uint32_t next_tag;
1402 do {
1403 ptr = ptr2;
1404 *field.Add() = UnalignedLoad<uint64_t>(ptr);
1405 ptr += size;
1406 if (!ctx->DataAvailable(ptr)) break;
1407 ptr2 = ReadTag(ptr, &next_tag);
1408 } while (next_tag == decoded_tag);
1409 } else {
1410 GOOGLE_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep32Bits));
1411 if (decoded_wiretype != WireFormatLite::WIRETYPE_FIXED32) {
1412 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1413 }
1414 auto& field = RefAt<RepeatedField<uint32_t>>(msg, entry.offset);
1415 constexpr auto size = sizeof(uint32_t);
1416 const char* ptr2 = ptr;
1417 uint32_t next_tag;
1418 do {
1419 ptr = ptr2;
1420 *field.Add() = UnalignedLoad<uint32_t>(ptr);
1421 ptr += size;
1422 if (!ctx->DataAvailable(ptr)) break;
1423 ptr2 = ReadTag(ptr, &next_tag);
1424 } while (next_tag == decoded_tag);
1425 }
1426
1427 PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
1428 }
1429
MpPackedFixed(PROTOBUF_TC_PARAM_DECL)1430 const char* TcParser::MpPackedFixed(PROTOBUF_TC_PARAM_DECL) {
1431 const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
1432 const uint16_t type_card = entry.type_card;
1433 const uint32_t decoded_wiretype = data.tag() & 7;
1434
1435 // Check for non-packed repeated fallback:
1436 if (decoded_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
1437 PROTOBUF_MUSTTAIL return MpRepeatedFixed(PROTOBUF_TC_PARAM_PASS);
1438 }
1439
1440 // Since ctx->ReadPackedFixed does not use TailCall<> or Return<>, sync any
1441 // pending hasbits now:
1442 SyncHasbits(msg, hasbits, table);
1443
1444 int size = ReadSize(&ptr);
1445 uint16_t rep = type_card & field_layout::kRepMask;
1446 if (rep == field_layout::kRep64Bits) {
1447 auto& field = RefAt<RepeatedField<uint64_t>>(msg, entry.offset);
1448 ptr = ctx->ReadPackedFixed(ptr, size, &field);
1449 } else {
1450 GOOGLE_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep32Bits));
1451 auto& field = RefAt<RepeatedField<uint32_t>>(msg, entry.offset);
1452 ptr = ctx->ReadPackedFixed(ptr, size, &field);
1453 }
1454
1455 if (ptr == nullptr) {
1456 return Error(PROTOBUF_TC_PARAM_PASS);
1457 }
1458 PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
1459 }
1460
MpVarint(PROTOBUF_TC_PARAM_DECL)1461 const char* TcParser::MpVarint(PROTOBUF_TC_PARAM_DECL) {
1462 const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
1463 const uint16_t type_card = entry.type_card;
1464 const uint16_t card = type_card & field_layout::kFcMask;
1465
1466 // Check for repeated parsing:
1467 if (card == field_layout::kFcRepeated) {
1468 PROTOBUF_MUSTTAIL return MpRepeatedVarint(PROTOBUF_TC_PARAM_PASS);
1469 }
1470 // Check for wire type mismatch:
1471 if ((data.tag() & 7) != WireFormatLite::WIRETYPE_VARINT) {
1472 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1473 }
1474 const uint16_t xform_val = type_card & field_layout::kTvMask;
1475 const bool is_zigzag = xform_val == field_layout::kTvZigZag;
1476 const bool is_validated_enum = xform_val & field_layout::kTvEnum;
1477
1478 // Parse the value:
1479 const char* ptr2 = ptr; // save for unknown enum case
1480 uint64_t tmp;
1481 ptr = ParseVarint(ptr, &tmp);
1482 if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
1483
1484 // Transform and/or validate the value
1485 uint16_t rep = type_card & field_layout::kRepMask;
1486 if (rep == field_layout::kRep64Bits) {
1487 if (is_zigzag) {
1488 tmp = WireFormatLite::ZigZagDecode64(tmp);
1489 }
1490 } else if (rep == field_layout::kRep32Bits) {
1491 if (is_validated_enum) {
1492 if (!EnumIsValidAux(tmp, xform_val, *table->field_aux(&entry))) {
1493 ptr = ptr2;
1494 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1495 }
1496 } else if (is_zigzag) {
1497 tmp = WireFormatLite::ZigZagDecode32(static_cast<uint32_t>(tmp));
1498 }
1499 }
1500
1501 // Mark the field as present:
1502 const bool is_oneof = card == field_layout::kFcOneof;
1503 if (card == field_layout::kFcOptional) {
1504 SetHas(table, entry, msg, hasbits);
1505 } else if (is_oneof) {
1506 ChangeOneof(table, entry, data.tag() >> 3, ctx, msg);
1507 }
1508
1509 if (rep == field_layout::kRep64Bits) {
1510 RefAt<uint64_t>(msg, entry.offset) = tmp;
1511 } else if (rep == field_layout::kRep32Bits) {
1512 RefAt<uint32_t>(msg, entry.offset) = static_cast<uint32_t>(tmp);
1513 } else {
1514 GOOGLE_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep8Bits));
1515 RefAt<bool>(msg, entry.offset) = static_cast<bool>(tmp);
1516 }
1517
1518 PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
1519 }
1520
MpRepeatedVarint(PROTOBUF_TC_PARAM_DECL)1521 const char* TcParser::MpRepeatedVarint(PROTOBUF_TC_PARAM_DECL) {
1522 const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
1523 auto type_card = entry.type_card;
1524 const uint32_t decoded_tag = data.tag();
1525 auto decoded_wiretype = decoded_tag & 7;
1526
1527 // Check for packed repeated fallback:
1528 if (decoded_wiretype == WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
1529 PROTOBUF_MUSTTAIL return MpPackedVarint(PROTOBUF_TC_PARAM_PASS);
1530 }
1531 // Check for wire type mismatch:
1532 if (decoded_wiretype != WireFormatLite::WIRETYPE_VARINT) {
1533 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1534 }
1535 uint16_t xform_val = (type_card & field_layout::kTvMask);
1536 const bool is_zigzag = xform_val == field_layout::kTvZigZag;
1537 const bool is_validated_enum = xform_val & field_layout::kTvEnum;
1538
1539 uint16_t rep = type_card & field_layout::kRepMask;
1540 if (rep == field_layout::kRep64Bits) {
1541 auto& field = RefAt<RepeatedField<uint64_t>>(msg, entry.offset);
1542 const char* ptr2 = ptr;
1543 uint32_t next_tag;
1544 do {
1545 uint64_t tmp;
1546 ptr = ParseVarint(ptr2, &tmp);
1547 if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
1548 field.Add(is_zigzag ? WireFormatLite::ZigZagDecode64(tmp) : tmp);
1549 if (!ctx->DataAvailable(ptr)) break;
1550 ptr2 = ReadTag(ptr, &next_tag);
1551 } while (next_tag == decoded_tag);
1552 } else if (rep == field_layout::kRep32Bits) {
1553 auto& field = RefAt<RepeatedField<uint32_t>>(msg, entry.offset);
1554 const char* ptr2 = ptr;
1555 uint32_t next_tag;
1556 do {
1557 uint64_t tmp;
1558 ptr = ParseVarint(ptr2, &tmp);
1559 if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
1560 if (is_validated_enum) {
1561 if (!EnumIsValidAux(tmp, xform_val, *table->field_aux(&entry))) {
1562 ptr = ptr2;
1563 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1564 }
1565 } else if (is_zigzag) {
1566 tmp = WireFormatLite::ZigZagDecode32(tmp);
1567 }
1568 field.Add(tmp);
1569 if (!ctx->DataAvailable(ptr)) break;
1570 ptr2 = ReadTag(ptr, &next_tag);
1571 } while (next_tag == decoded_tag);
1572 } else {
1573 GOOGLE_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep8Bits));
1574 auto& field = RefAt<RepeatedField<bool>>(msg, entry.offset);
1575 const char* ptr2 = ptr;
1576 uint32_t next_tag;
1577 do {
1578 uint64_t tmp;
1579 ptr = ParseVarint(ptr2, &tmp);
1580 if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
1581 field.Add(static_cast<bool>(tmp));
1582 if (!ctx->DataAvailable(ptr)) break;
1583 ptr2 = ReadTag(ptr, &next_tag);
1584 } while (next_tag == decoded_tag);
1585 }
1586
1587 PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
1588 }
1589
MpPackedVarint(PROTOBUF_TC_PARAM_DECL)1590 const char* TcParser::MpPackedVarint(PROTOBUF_TC_PARAM_DECL) {
1591 const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
1592 auto type_card = entry.type_card;
1593 auto decoded_wiretype = data.tag() & 7;
1594
1595 // Check for non-packed repeated fallback:
1596 if (decoded_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
1597 PROTOBUF_MUSTTAIL return MpRepeatedVarint(PROTOBUF_TC_PARAM_PASS);
1598 }
1599 uint16_t xform_val = (type_card & field_layout::kTvMask);
1600 const bool is_zigzag = xform_val == field_layout::kTvZigZag;
1601 const bool is_validated_enum = xform_val & field_layout::kTvEnum;
1602 if (is_validated_enum) {
1603 // TODO(b/206890171): handle enums
1604 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1605 }
1606
1607 // Since ctx->ReadPackedFixed does not use TailCall<> or Return<>, sync any
1608 // pending hasbits now:
1609 SyncHasbits(msg, hasbits, table);
1610
1611 uint16_t rep = type_card & field_layout::kRepMask;
1612 if (rep == field_layout::kRep64Bits) {
1613 auto* field = &RefAt<RepeatedField<uint64_t>>(msg, entry.offset);
1614 return ctx->ReadPackedVarint(ptr, [field, is_zigzag](uint64_t value) {
1615 field->Add(is_zigzag ? WireFormatLite::ZigZagDecode64(value) : value);
1616 });
1617 } else if (rep == field_layout::kRep32Bits) {
1618 auto* field = &RefAt<RepeatedField<uint32_t>>(msg, entry.offset);
1619 return ctx->ReadPackedVarint(ptr, [field, is_zigzag](uint64_t value) {
1620 field->Add(is_zigzag ? WireFormatLite::ZigZagDecode32(
1621 static_cast<uint32_t>(value))
1622 : value);
1623 });
1624 } else {
1625 GOOGLE_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep8Bits));
1626 auto* field = &RefAt<RepeatedField<bool>>(msg, entry.offset);
1627 return ctx->ReadPackedVarint(
1628 ptr, [field](uint64_t value) { field->Add(value); });
1629 }
1630
1631 return Error(PROTOBUF_TC_PARAM_PASS);
1632 }
1633
MpVerifyUtf8(StringPiece wire_bytes,const TcParseTableBase * table,const FieldEntry & entry,uint16_t xform_val)1634 bool TcParser::MpVerifyUtf8(StringPiece wire_bytes,
1635 const TcParseTableBase* table,
1636 const FieldEntry& entry, uint16_t xform_val) {
1637 if (xform_val == field_layout::kTvUtf8) {
1638 if (!IsStructurallyValidUTF8(wire_bytes)) {
1639 PrintUTF8ErrorLog(MessageName(table), FieldName(table, &entry), "parsing",
1640 false);
1641 return false;
1642 }
1643 return true;
1644 }
1645 #ifndef NDEBUG
1646 if (xform_val == field_layout::kTvUtf8Debug) {
1647 if (!IsStructurallyValidUTF8(wire_bytes)) {
1648 PrintUTF8ErrorLog(MessageName(table), FieldName(table, &entry), "parsing",
1649 false);
1650 }
1651 }
1652 #endif // NDEBUG
1653 return true;
1654 }
1655
MpString(PROTOBUF_TC_PARAM_DECL)1656 const char* TcParser::MpString(PROTOBUF_TC_PARAM_DECL) {
1657 const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
1658 const uint16_t type_card = entry.type_card;
1659 const uint16_t card = type_card & field_layout::kFcMask;
1660 const uint32_t decoded_wiretype = data.tag() & 7;
1661
1662 if (decoded_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
1663 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1664 }
1665 if (card == field_layout::kFcRepeated) {
1666 PROTOBUF_MUSTTAIL return MpRepeatedString(PROTOBUF_TC_PARAM_PASS);
1667 }
1668 const uint16_t xform_val = type_card & field_layout::kTvMask;
1669 const uint16_t rep = type_card & field_layout::kRepMask;
1670 if (rep == field_layout::kRepIString) {
1671 // TODO(b/198211897): support InilnedStringField.
1672 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1673 }
1674
1675 // Mark the field as present:
1676 const bool is_oneof = card == field_layout::kFcOneof;
1677 bool need_init = false;
1678 if (card == field_layout::kFcOptional) {
1679 SetHas(table, entry, msg, hasbits);
1680 } else if (is_oneof) {
1681 need_init = ChangeOneof(table, entry, data.tag() >> 3, ctx, msg);
1682 }
1683
1684 bool is_valid = false;
1685 Arena* arena = ctx->data().arena;
1686 switch (rep) {
1687 case field_layout::kRepAString: {
1688 auto& field = RefAt<ArenaStringPtr>(msg, entry.offset);
1689 if (need_init) field.InitDefault();
1690 if (arena) {
1691 ptr = ctx->ReadArenaString(ptr, &field, arena);
1692 } else {
1693 std::string* str = field.MutableNoCopy(nullptr);
1694 ptr = InlineGreedyStringParser(str, ptr, ctx);
1695 }
1696 if (!ptr) break;
1697 is_valid = MpVerifyUtf8(field.Get(), table, entry, xform_val);
1698 break;
1699 }
1700
1701 case field_layout::kRepIString: {
1702 break;
1703 }
1704 }
1705
1706 if (ptr == nullptr || !is_valid) {
1707 return Error(PROTOBUF_TC_PARAM_PASS);
1708 }
1709 return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
1710 }
1711
MpRepeatedString(PROTOBUF_TC_PARAM_DECL)1712 const char* TcParser::MpRepeatedString(PROTOBUF_TC_PARAM_DECL) {
1713 const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
1714 const uint16_t type_card = entry.type_card;
1715 const uint32_t decoded_tag = data.tag();
1716 const uint32_t decoded_wiretype = decoded_tag & 7;
1717
1718 if (decoded_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
1719 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1720 }
1721
1722 const uint16_t rep = type_card & field_layout::kRepMask;
1723 const uint16_t xform_val = type_card & field_layout::kTvMask;
1724 switch (rep) {
1725 case field_layout::kRepSString: {
1726 auto& field = RefAt<RepeatedPtrField<std::string>>(msg, entry.offset);
1727 const char* ptr2 = ptr;
1728 uint32_t next_tag;
1729 do {
1730 ptr = ptr2;
1731 std::string* str = field.Add();
1732 ptr = InlineGreedyStringParser(str, ptr, ctx);
1733 if (PROTOBUF_PREDICT_FALSE(
1734 ptr == nullptr ||
1735 !MpVerifyUtf8(*str, table, entry, xform_val))) {
1736 return Error(PROTOBUF_TC_PARAM_PASS);
1737 }
1738 if (!ctx->DataAvailable(ptr)) break;
1739 ptr2 = ReadTag(ptr, &next_tag);
1740 } while (next_tag == decoded_tag);
1741 break;
1742 }
1743
1744 #ifndef NDEBUG
1745 default:
1746 GOOGLE_LOG(FATAL) << "Unsupported repeated string rep: " << rep;
1747 break;
1748 #endif
1749 }
1750
1751 return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
1752 }
1753
MpMessage(PROTOBUF_TC_PARAM_DECL)1754 const char* TcParser::MpMessage(PROTOBUF_TC_PARAM_DECL) {
1755 const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
1756 const uint16_t type_card = entry.type_card;
1757 const uint16_t card = type_card & field_layout::kFcMask;
1758
1759 // Check for repeated parsing:
1760 if (card == field_layout::kFcRepeated) {
1761 PROTOBUF_MUSTTAIL return MpRepeatedMessage(PROTOBUF_TC_PARAM_PASS);
1762 }
1763
1764 const uint32_t decoded_tag = data.tag();
1765 const uint32_t decoded_wiretype = decoded_tag & 7;
1766 const uint16_t rep = type_card & field_layout::kRepMask;
1767 const bool is_group = rep == field_layout::kRepGroup;
1768
1769 // Validate wiretype:
1770 switch (rep) {
1771 case field_layout::kRepMessage:
1772 if (decoded_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
1773 goto fallback;
1774 }
1775 break;
1776 case field_layout::kRepGroup:
1777 if (decoded_wiretype != WireFormatLite::WIRETYPE_START_GROUP) {
1778 goto fallback;
1779 }
1780 break;
1781 default: {
1782 fallback:
1783 // Lazy and implicit weak fields are handled by generated code:
1784 // TODO(b/210762816): support these.
1785 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1786 }
1787 }
1788
1789 const bool is_oneof = card == field_layout::kFcOneof;
1790 bool need_init = false;
1791 if (card == field_layout::kFcOptional) {
1792 SetHas(table, entry, msg, hasbits);
1793 } else if (is_oneof) {
1794 need_init = ChangeOneof(table, entry, data.tag() >> 3, ctx, msg);
1795 }
1796 MessageLite*& field = RefAt<MessageLite*>(msg, entry.offset);
1797 if (need_init || field == nullptr) {
1798 const MessageLite* default_instance =
1799 table->field_aux(&entry)->message_default;
1800 field = default_instance->New(ctx->data().arena);
1801 }
1802 SyncHasbits(msg, hasbits, table);
1803 if (is_group) {
1804 return ctx->ParseGroup(field, ptr, decoded_tag);
1805 }
1806 return ctx->ParseMessage(field, ptr);
1807 }
1808
MpRepeatedMessage(PROTOBUF_TC_PARAM_DECL)1809 const char* TcParser::MpRepeatedMessage(PROTOBUF_TC_PARAM_DECL) {
1810 const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
1811 const uint16_t type_card = entry.type_card;
1812 GOOGLE_DCHECK_EQ(type_card & field_layout::kFcMask,
1813 static_cast<uint16_t>(field_layout::kFcRepeated));
1814 const uint32_t decoded_tag = data.tag();
1815 const uint32_t decoded_wiretype = decoded_tag & 7;
1816 const uint16_t rep = type_card & field_layout::kRepMask;
1817 const bool is_group = rep == field_layout::kRepGroup;
1818
1819 // Validate wiretype:
1820 switch (rep) {
1821 case field_layout::kRepMessage:
1822 if (decoded_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
1823 goto fallback;
1824 }
1825 break;
1826 case field_layout::kRepGroup:
1827 if (decoded_wiretype != WireFormatLite::WIRETYPE_START_GROUP) {
1828 goto fallback;
1829 }
1830 break;
1831 default: {
1832 fallback:
1833 // Lazy and implicit weak fields are handled by generated code:
1834 // TODO(b/210762816): support these.
1835 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1836 }
1837 }
1838
1839 SyncHasbits(msg, hasbits, table);
1840 const MessageLite* default_instance =
1841 table->field_aux(&entry)->message_default;
1842 auto& field = RefAt<RepeatedPtrFieldBase>(msg, entry.offset);
1843 MessageLite* value =
1844 field.Add<GenericTypeHandler<MessageLite>>(default_instance);
1845 if (is_group) {
1846 return ctx->ParseGroup(value, ptr, decoded_tag);
1847 }
1848 return ctx->ParseMessage(value, ptr);
1849 }
1850
MpMap(PROTOBUF_TC_PARAM_DECL)1851 const char* TcParser::MpMap(PROTOBUF_TC_PARAM_DECL) {
1852 const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
1853 (void)entry;
1854 PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
1855 }
1856
1857 } // namespace internal
1858 } // namespace protobuf
1859 } // namespace google
1860