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 | |
49 | namespace google { |
50 | namespace protobuf { |
51 | namespace internal { |
52 | |
53 | // Additional information about this field: |
54 | struct 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 | |
106 | struct TcParseTableBase; |
107 | |
108 | // TailCallParseFunc is the function pointer type used in the tailcall table. |
109 | typedef const char* (*TailCallParseFunc)(PROTOBUF_TC_PARAM_DECL); |
110 | |
111 | namespace field_layout { |
112 | struct 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. |
124 | struct 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 | |
243 | static_assert(sizeof(TcParseTableBase::FastFieldEntry) <= 16, |
244 | "Fast field entry is too big." ); |
245 | static_assert(sizeof(TcParseTableBase::FieldEntry) <= 16, |
246 | "Field entry is too big." ); |
247 | |
248 | template <size_t kFastTableSizeLog2, size_t kNumFieldEntries = 0, |
249 | size_t kNumFieldAux = 0, size_t kNameTableSize = 0, |
250 | size_t kFieldLookupSize = 2> |
251 | struct TcParseTable { |
252 | TcParseTableBase ; |
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. |
275 | template <size_t kFastTableSizeLog2, size_t kNumFieldEntries, |
276 | size_t kNameTableSize, size_t kFieldLookupSize> |
277 | struct TcParseTable<kFastTableSizeLog2, kNumFieldEntries, 0, kNameTableSize, |
278 | kFieldLookupSize> { |
279 | TcParseTableBase ; |
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. |
289 | template <size_t kNameTableSize, size_t kFieldLookupSize> |
290 | struct TcParseTable<0, 0, 0, kNameTableSize, kFieldLookupSize> { |
291 | TcParseTableBase ; |
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 | |
299 | static_assert(std::is_standard_layout<TcParseTable<1>>::value, |
300 | "TcParseTable must be standard layout." ); |
301 | |
302 | static_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 | |