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// This file contains declarations needed in generated headers for messages
32// that use tail-call table parsing. Everything in this file is for internal
33// use only.
34
35#ifndef GOOGLE_PROTOBUF_GENERATED_MESSAGE_TCTABLE_DECL_H__
36#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_TCTABLE_DECL_H__
37
38#include <array>
39#include <cstddef>
40#include <cstdint>
41#include <type_traits>
42
43#include <google/protobuf/message_lite.h>
44#include <google/protobuf/parse_context.h>
45
46// Must come last:
47#include <google/protobuf/port_def.inc>
48
49namespace google {
50namespace protobuf {
51namespace internal {
52
53// Additional information about this field:
54struct TcFieldData {
55 constexpr TcFieldData() : data(0) {}
56
57 // Fast table entry constructor:
58 constexpr TcFieldData(uint16_t coded_tag, uint8_t hasbit_idx, uint8_t aux_idx,
59 uint16_t offset)
60 : data(uint64_t{offset} << 48 | //
61 uint64_t{aux_idx} << 24 | //
62 uint64_t{hasbit_idx} << 16 | //
63 uint64_t{coded_tag}) {}
64
65 // Fields used in fast table parsing:
66 //
67 // Bit:
68 // +-----------+-------------------+
69 // |63 .. 32|31 .. 0|
70 // +---------------+---------------+
71 // : . : . : . 16|=======| [16] coded_tag()
72 // : . : . : 24|===| . : [ 8] hasbit_idx()
73 // : . : . 32|===| : . : [ 8] aux_idx()
74 // : . 48:---.---: . : . : [16] (unused)
75 // |=======| . : . : . : [16] offset()
76 // +-----------+-------------------+
77 // |63 .. 32|31 .. 0|
78 // +---------------+---------------+
79
80 template <typename TagType = uint16_t>
81 TagType coded_tag() const {
82 return static_cast<TagType>(data);
83 }
84 uint8_t hasbit_idx() const { return static_cast<uint8_t>(data >> 16); }
85 uint8_t aux_idx() const { return static_cast<uint8_t>(data >> 24); }
86 uint16_t offset() const { return static_cast<uint16_t>(data >> 48); }
87
88 // Fields used in mini table parsing:
89 //
90 // Bit:
91 // +-----------+-------------------+
92 // |63 .. 32|31 .. 0|
93 // +---------------+---------------+
94 // : . : . |===============| [32] tag() (decoded)
95 // |===============| . : . : [32] entry_offset()
96 // +-----------+-------------------+
97 // |63 .. 32|31 .. 0|
98 // +---------------+---------------+
99
100 uint32_t tag() const { return static_cast<uint32_t>(data); }
101 uint32_t entry_offset() const { return static_cast<uint32_t>(data >> 32); }
102
103 uint64_t data;
104};
105
106struct TcParseTableBase;
107
108// TailCallParseFunc is the function pointer type used in the tailcall table.
109typedef const char* (*TailCallParseFunc)(PROTOBUF_TC_PARAM_DECL);
110
111namespace field_layout {
112struct Offset {
113 uint32_t off;
114};
115} // namespace field_layout
116
117#if defined(_MSC_VER) && !defined(_WIN64)
118#pragma warning(push)
119// TcParseTableBase is intentionally overaligned on 32 bit targets.
120#pragma warning(disable : 4324)
121#endif
122
123// Base class for message-level table with info for the tail-call parser.
124struct alignas(uint64_t) TcParseTableBase {
125 // Common attributes for message layout:
126 uint16_t has_bits_offset;
127 uint16_t extension_offset;
128 uint32_t extension_range_low;
129 uint32_t extension_range_high;
130 uint32_t max_field_number;
131 uint8_t fast_idx_mask;
132 uint16_t lookup_table_offset;
133 uint32_t skipmap32;
134 uint32_t field_entries_offset;
135 uint16_t num_field_entries;
136
137 uint16_t num_aux_entries;
138 uint32_t aux_offset;
139
140 const MessageLite* default_instance;
141
142 // Handler for fields which are not handled by table dispatch.
143 TailCallParseFunc fallback;
144
145 // This constructor exactly follows the field layout, so it's technically
146 // not necessary. However, it makes it much much easier to add or re-arrange
147 // fields, because it can be overloaded with an additional constructor,
148 // temporarily allowing both old and new protocol buffer headers to be
149 // compiled.
150 constexpr TcParseTableBase(
151 uint16_t has_bits_offset, uint16_t extension_offset,
152 uint32_t extension_range_low, uint32_t extension_range_high,
153 uint32_t max_field_number, uint8_t fast_idx_mask,
154 uint16_t lookup_table_offset, uint32_t skipmap32,
155 uint32_t field_entries_offset, uint16_t num_field_entries,
156 uint16_t num_aux_entries, uint32_t aux_offset,
157 const MessageLite* default_instance, TailCallParseFunc fallback)
158 : has_bits_offset(has_bits_offset),
159 extension_offset(extension_offset),
160 extension_range_low(extension_range_low),
161 extension_range_high(extension_range_high),
162 max_field_number(max_field_number),
163 fast_idx_mask(fast_idx_mask),
164 lookup_table_offset(lookup_table_offset),
165 skipmap32(skipmap32),
166 field_entries_offset(field_entries_offset),
167 num_field_entries(num_field_entries),
168 num_aux_entries(num_aux_entries),
169 aux_offset(aux_offset),
170 default_instance(default_instance),
171 fallback(fallback) {}
172
173 // Table entry for fast-path tailcall dispatch handling.
174 struct FastFieldEntry {
175 // Target function for dispatch:
176 TailCallParseFunc target;
177 // Field data used during parse:
178 TcFieldData bits;
179 };
180 // There is always at least one table entry.
181 const FastFieldEntry* fast_entry(size_t idx) const {
182 return reinterpret_cast<const FastFieldEntry*>(this + 1) + idx;
183 }
184
185 // Returns a begin iterator (pointer) to the start of the field lookup table.
186 const uint16_t* field_lookup_begin() const {
187 return reinterpret_cast<const uint16_t*>(reinterpret_cast<uintptr_t>(this) +
188 lookup_table_offset);
189 }
190
191 // Field entry for all fields.
192 struct FieldEntry {
193 uint32_t offset; // offset in the message object
194 int32_t has_idx; // has-bit index
195 uint16_t aux_idx; // index for `field_aux`.
196 uint16_t type_card; // `FieldType` and `Cardinality` (see _impl.h)
197 };
198
199 // Returns a begin iterator (pointer) to the start of the field entries array.
200 const FieldEntry* field_entries_begin() const {
201 return reinterpret_cast<const FieldEntry*>(
202 reinterpret_cast<uintptr_t>(this) + field_entries_offset);
203 }
204
205 // Auxiliary entries for field types that need extra information.
206 union FieldAux {
207 constexpr FieldAux() : message_default(nullptr) {}
208 constexpr FieldAux(bool (*enum_validator)(int))
209 : enum_validator(enum_validator) {}
210 constexpr FieldAux(field_layout::Offset off) : offset(off.off) {}
211 constexpr FieldAux(int16_t range_start, uint16_t range_length)
212 : enum_range{.start: range_start, .length: range_length} {}
213 constexpr FieldAux(const MessageLite* msg) : message_default(msg) {}
214 bool (*enum_validator)(int);
215 struct {
216 int16_t start; // minimum enum number (if it fits)
217 uint16_t length; // length of range (i.e., max = start + length - 1)
218 } enum_range;
219 uint32_t offset;
220 const MessageLite* message_default;
221 };
222 const FieldAux* field_aux(uint32_t idx) const {
223 return reinterpret_cast<const FieldAux*>(reinterpret_cast<uintptr_t>(this) +
224 aux_offset) +
225 idx;
226 }
227 const FieldAux* field_aux(const FieldEntry* entry) const {
228 return field_aux(idx: entry->aux_idx);
229 }
230
231 // Field name data
232 const char* name_data() const {
233 return reinterpret_cast<const char*>(reinterpret_cast<uintptr_t>(this) +
234 aux_offset +
235 num_aux_entries * sizeof(FieldAux));
236 }
237};
238
239#if defined(_MSC_VER) && !defined(_WIN64)
240#pragma warning(pop)
241#endif
242
243static_assert(sizeof(TcParseTableBase::FastFieldEntry) <= 16,
244 "Fast field entry is too big.");
245static_assert(sizeof(TcParseTableBase::FieldEntry) <= 16,
246 "Field entry is too big.");
247
248template <size_t kFastTableSizeLog2, size_t kNumFieldEntries = 0,
249 size_t kNumFieldAux = 0, size_t kNameTableSize = 0,
250 size_t kFieldLookupSize = 2>
251struct TcParseTable {
252 TcParseTableBase header;
253
254 // Entries for each field.
255 //
256 // Fields are indexed by the lowest bits of their field number. The field
257 // number is masked to fit inside the table. Note that the parsing logic
258 // generally calls `TailCallParseTableBase::fast_entry()` instead of accessing
259 // this field directly.
260 std::array<TcParseTableBase::FastFieldEntry, (1 << kFastTableSizeLog2)>
261 fast_entries;
262
263 // Just big enough to find all the field entries.
264 std::array<uint16_t, kFieldLookupSize> field_lookup_table;
265 // Entries for all fields:
266 std::array<TcParseTableBase::FieldEntry, kNumFieldEntries> field_entries;
267 std::array<TcParseTableBase::FieldAux, kNumFieldAux> aux_entries;
268 std::array<char, kNameTableSize> field_names;
269};
270
271// Partial specialization: if there are no aux entries, there will be no array.
272// In C++, arrays cannot have length 0, but (C++11) std::array<T, 0> is valid.
273// However, different implementations have different sizeof(std::array<T, 0>).
274// Skipping the member makes offset computations portable.
275template <size_t kFastTableSizeLog2, size_t kNumFieldEntries,
276 size_t kNameTableSize, size_t kFieldLookupSize>
277struct TcParseTable<kFastTableSizeLog2, kNumFieldEntries, 0, kNameTableSize,
278 kFieldLookupSize> {
279 TcParseTableBase header;
280 std::array<TcParseTableBase::FastFieldEntry, (1 << kFastTableSizeLog2)>
281 fast_entries;
282 std::array<uint16_t, kFieldLookupSize> field_lookup_table;
283 std::array<TcParseTableBase::FieldEntry, kNumFieldEntries> field_entries;
284 std::array<char, kNameTableSize> field_names;
285};
286
287// Partial specialization: if there are no fields at all, then we can save space
288// by skipping the field numbers and entries.
289template <size_t kNameTableSize, size_t kFieldLookupSize>
290struct TcParseTable<0, 0, 0, kNameTableSize, kFieldLookupSize> {
291 TcParseTableBase header;
292 // N.B.: the fast entries are sized by log2, so 2**0 fields = 1 entry.
293 // The fast parsing loop will always use this entry, so it must be present.
294 std::array<TcParseTableBase::FastFieldEntry, 1> fast_entries;
295 std::array<uint16_t, kFieldLookupSize> field_lookup_table;
296 std::array<char, kNameTableSize> field_names;
297};
298
299static_assert(std::is_standard_layout<TcParseTable<1>>::value,
300 "TcParseTable must be standard layout.");
301
302static_assert(offsetof(TcParseTable<1>, fast_entries) ==
303 sizeof(TcParseTableBase),
304 "Table entries must be laid out after TcParseTableBase.");
305
306} // namespace internal
307} // namespace protobuf
308} // namespace google
309
310#include <google/protobuf/port_undef.inc>
311
312#endif // GOOGLE_PROTOBUF_GENERATED_MESSAGE_TCTABLE_DECL_H__
313