1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #pragma once
6 
7 namespace fidl {
8 namespace internal {
9 
10 // Some assumptions about data type layout.
11 static_assert(offsetof(fidl_string_t, size) == 0u, "");
12 static_assert(offsetof(fidl_string_t, data) == 8u, "");
13 
14 static_assert(offsetof(fidl_vector_t, count) == 0u, "");
15 static_assert(offsetof(fidl_vector_t, data) == 8u, "");
16 
17 static_assert(ZX_HANDLE_INVALID == FIDL_HANDLE_ABSENT, "");
18 
19 template <bool kConst, class U>
20 struct SetPtrConst;
21 
22 template <class U>
23 struct SetPtrConst<false, U> {
24     typedef U* type;
25 };
26 
27 template <class U>
28 struct SetPtrConst<true, U> {
29     typedef const U* type;
30 };
31 
32 // Walks over a FIDL buffer and validates/encodes/decodes it per-Derived.
33 //
34 // kMutating controls whether this deals with mutable bytes or immutable bytes
35 // (validation wants immutable, encode/decode wants mutable)
36 //
37 // kContinueAfterErrors controls whether parsing is continued upon failure (encode needs this to
38 // see all available handles).
39 //
40 // Derived should offer the following methods:
41 //
42 //   const? uint8_t* bytes() - returns the start of the buffer of bytes
43 //   uint32_t num_bytes() - returns the number of bytes in said buffer
44 //   uint32_t num_handles() - returns the number of handles that are claimable
45 //   bool ValidateOutOfLineStorageClaim(const void* a, const void* b)
46 //      - returns true if a legally points to b
47 //   void UnclaimedHandle(zx_handle_t*) - notes that a handle was skipped
48 //   void ClaimedHandle(zx_handle_t*, uint32_t idx) - notes that a handle was claimed
49 //   PointerState GetPointerState(const void* ptr) - returns whether a pointer is present or not
50 //   HandleState GetHandleState(zx_handle_t) - returns if a handle is present or not
51 //   void UpdatePointer(T**p, T*v) - mutates a pointer representation for a present pointer
52 //   void SetError(const char* error_msg) - flags that an error occurred
53 template <class Derived, bool kMutating, bool kContinueAfterErrors>
54 class BufferWalker {
55 public:
56     explicit BufferWalker(const fidl_type* type)
57         : type_(type) {}
58 
59     void Walk() {
60         // The first decode is special. It must be a struct or a table.
61         // We need to know the size of the first element to compute the start of
62         // the out-of-line allocations.
63 
64         if (type_ == nullptr) {
65             SetError("Cannot decode a null fidl type");
66             return;
67         }
68 
69         if (bytes() == nullptr) {
70             SetError("Cannot decode null bytes");
71             return;
72         }
73 
74         switch (type_->type_tag) {
75         case fidl::kFidlTypeStruct:
76             if (num_bytes() < type_->coded_struct.size) {
77                 SetError("Message size is smaller than expected");
78                 return;
79             }
80             out_of_line_offset_ = static_cast<uint32_t>(fidl::FidlAlign(type_->coded_struct.size));
81             break;
82         case fidl::kFidlTypeTable:
83             if (num_bytes() < sizeof(fidl_vector_t)) {
84                 SetError("Message size is smaller than expected");
85                 return;
86             }
87             out_of_line_offset_ = static_cast<uint32_t>(fidl::FidlAlign(sizeof(fidl_vector_t)));
88             break;
89         default:
90             SetError("Message must be a struct or a table");
91             return;
92         }
93 
94         Push(Frame::DoneSentinel());
95         Push(Frame(type_, 0u));
96 
97 // Macro to insert the relevant goop required to support two control flows here:
98 // one where we keep reading after error, and another where we return immediately.
99 // No runtime overhead thanks to if constexpr magic.
100 #define FIDL_POP_AND_CONTINUE_OR_RETURN \
101     if (kContinueAfterErrors) {         \
102         Pop();                          \
103         continue;                       \
104     } else {                            \
105         return;                         \
106     }
107 
108         for (;;) {
109             Frame* frame = Peek();
110 
111             switch (frame->state) {
112             case Frame::kStateStruct: {
113                 const uint32_t field_index = frame->NextStructField();
114                 if (field_index == frame->struct_state.field_count) {
115                     Pop();
116                     continue;
117                 }
118                 const fidl::FidlField& field = frame->struct_state.fields[field_index];
119                 const fidl_type_t* field_type = field.type;
120                 const uint32_t field_offset = frame->offset + field.offset;
121                 if (!Push(Frame(field_type, field_offset))) {
122                     SetError("recursion depth exceeded processing struct");
123                     FIDL_POP_AND_CONTINUE_OR_RETURN;
124                 }
125                 continue;
126             }
127             case Frame::kStateStructPointer: {
128                 switch (GetPointerState(TypedAt<void>(frame->offset))) {
129                 case PointerState::PRESENT:
130                     break;
131                 case PointerState::ABSENT:
132                     Pop();
133                     continue;
134                 default:
135                     SetError("Tried to decode a bad struct pointer");
136                     FIDL_POP_AND_CONTINUE_OR_RETURN;
137                 }
138                 auto struct_ptr_ptr = TypedAt<void*>(frame->offset);
139                 if (!ClaimOutOfLineStorage(frame->struct_pointer_state.struct_type->size,
140                                            *struct_ptr_ptr, &frame->offset)) {
141                     SetError("message wanted to store too large of a nullable struct");
142                     FIDL_POP_AND_CONTINUE_OR_RETURN;
143                 }
144                 UpdatePointer(struct_ptr_ptr, TypedAt<void>(frame->offset));
145                 const fidl::FidlCodedStruct* coded_struct = frame->struct_pointer_state.struct_type;
146                 *frame = Frame(coded_struct, frame->offset);
147                 continue;
148             }
149             case Frame::kStateTable: {
150                 if (frame->field == 0u) {
151                     auto envelope_vector_ptr = TypedAt<fidl_vector_t>(frame->offset);
152                     switch (GetPointerState(&envelope_vector_ptr->data)) {
153                     case PointerState::PRESENT:
154                         break;
155                     case PointerState::ABSENT:
156                         SetError("Table data cannot be absent");
157                         FIDL_POP_AND_CONTINUE_OR_RETURN;
158                     default:
159                         SetError("message tried to decode a non-present vector");
160                         FIDL_POP_AND_CONTINUE_OR_RETURN;
161                     }
162                     uint32_t size;
163                     if (mul_overflow(envelope_vector_ptr->count, 2 * sizeof(uint64_t), &size)) {
164                         SetError("integer overflow calculating table size");
165                         FIDL_POP_AND_CONTINUE_OR_RETURN;
166                     }
167                     if (!ClaimOutOfLineStorage(size, envelope_vector_ptr->data, &frame->offset)) {
168                         SetError("message wanted to store too large of a table");
169                         FIDL_POP_AND_CONTINUE_OR_RETURN;
170                     }
171                     UpdatePointer(&envelope_vector_ptr->data, TypedAt<void>(frame->offset));
172                     frame->field = 1;
173                     frame->table_state.known_index = 0;
174                     frame->table_state.present_count = static_cast<uint32_t>(envelope_vector_ptr->count);
175                     frame->table_state.end_offset = out_of_line_offset_;
176                     frame->table_state.end_handle = handle_idx_;
177                     continue;
178                 }
179                 if (frame->table_state.end_offset != out_of_line_offset_) {
180                     SetError("Table field was mis-sized");
181                     FIDL_POP_AND_CONTINUE_OR_RETURN;
182                 }
183                 if (frame->table_state.end_handle != handle_idx_) {
184                     SetError("Table handles were mis-sized");
185                     FIDL_POP_AND_CONTINUE_OR_RETURN;
186                 }
187                 if (frame->field > frame->table_state.present_count) {
188                     Pop();
189                     continue;
190                 }
191                 const fidl::FidlTableField* known_field = nullptr;
192                 if (frame->table_state.known_index < frame->table_state.field_count) {
193                     const fidl::FidlTableField* field =
194                         &frame->table_state.fields[frame->table_state.known_index];
195                     if (field->ordinal == frame->field) {
196                         known_field = field;
197                         frame->table_state.known_index++;
198                     }
199                 }
200                 const uint32_t tag_offset = static_cast<uint32_t>(
201                     frame->offset + (frame->field - 1) * 2 * sizeof(uint64_t));
202                 const uint32_t data_offset = static_cast<uint32_t>(
203                     tag_offset + sizeof(uint64_t));
204                 const uint64_t packed_sizes = *TypedAt<uint64_t>(tag_offset);
205                 frame->field++;
206                 switch (GetPointerState(TypedAt<void>(data_offset))) {
207                 case PointerState::PRESENT:
208                     if (packed_sizes != 0)
209                         break; // expected
210 
211                     SetError("Table envelope has present data pointer, but no data, and no handles");
212                     FIDL_POP_AND_CONTINUE_OR_RETURN;
213                 case PointerState::ABSENT:
214                     if (packed_sizes == 0)
215                         continue; // skip
216 
217                     SetError("Table envelope has absent data pointer, yet has data and/or handles");
218                     FIDL_POP_AND_CONTINUE_OR_RETURN;
219                 default:
220                     SetError("Table envelope has bad data pointer");
221                     FIDL_POP_AND_CONTINUE_OR_RETURN;
222                 }
223                 uint32_t offset;
224                 uint32_t handles;
225                 const uint32_t table_bytes = static_cast<uint32_t>(packed_sizes & 0xffffffffu);
226                 const uint32_t table_handles = static_cast<uint32_t>(packed_sizes >> 32);
227                 if (add_overflow(out_of_line_offset_, table_bytes, &offset) || offset > num_bytes()) {
228                     SetError("integer overflow decoding table field");
229                     FIDL_POP_AND_CONTINUE_OR_RETURN;
230                 }
231                 if (add_overflow(handle_idx_, table_handles, &handles) ||
232                     handles > num_handles()) {
233                     SetError("integer overflow decoding table handles");
234                     FIDL_POP_AND_CONTINUE_OR_RETURN;
235                 }
236                 frame->table_state.end_offset = offset;
237                 frame->table_state.end_handle = handles;
238                 if (known_field != nullptr) {
239                     const fidl_type_t* field_type = known_field->type;
240                     uint32_t field_offset;
241                     if (!ClaimOutOfLineStorage(TypeSize(field_type), TypedAt<void*>(data_offset), &field_offset)) {
242                         SetError("table wanted too many bytes in field");
243                         FIDL_POP_AND_CONTINUE_OR_RETURN;
244                     }
245                     UpdatePointer(TypedAt<void*>(data_offset), TypedAt<void>(field_offset));
246                     if (!Push(Frame(field_type, field_offset))) {
247                         SetError("recursion depth exceeded decoding table");
248                         FIDL_POP_AND_CONTINUE_OR_RETURN;
249                     }
250                 } else {
251                     // Table data will not be processed: discard it.
252                     uint32_t field_offset;
253                     if (!ClaimOutOfLineStorage(table_bytes, TypedAt<void*>(data_offset), &field_offset)) {
254                         SetError("table wanted too many bytes in field");
255                         FIDL_POP_AND_CONTINUE_OR_RETURN;
256                     }
257                     UpdatePointer(TypedAt<void*>(data_offset), TypedAt<void>(field_offset));
258                     for (uint32_t i = 0; i < table_handles; i++) {
259                         if (!ClaimHandle(nullptr)) {
260                             SetError("expected handle not present");
261                             FIDL_POP_AND_CONTINUE_OR_RETURN;
262                         }
263                     }
264                 }
265                 continue;
266             }
267             case Frame::kStateTablePointer: {
268                 switch (GetPointerState(TypedAt<void>(frame->offset))) {
269                 case PointerState::PRESENT:
270                     break;
271                 case PointerState::ABSENT:
272                     Pop();
273                     continue;
274                 default:
275                     SetError("Tried to decode a bad table pointer");
276                     FIDL_POP_AND_CONTINUE_OR_RETURN;
277                 }
278                 auto table_ptr_ptr = TypedAt<void*>(frame->offset);
279                 if (!ClaimOutOfLineStorage(sizeof(fidl_vector_t), *table_ptr_ptr, &frame->offset)) {
280                     SetError("message wanted to store too large of a nullable table");
281                     FIDL_POP_AND_CONTINUE_OR_RETURN;
282                 }
283                 UpdatePointer(table_ptr_ptr, TypedAt<void>(frame->offset));
284                 const fidl::FidlCodedTable* coded_table = frame->table_pointer_state.table_type;
285                 *frame = Frame(coded_table, frame->offset);
286                 continue;
287             }
288             case Frame::kStateUnion: {
289                 fidl_union_tag_t union_tag = *TypedAt<fidl_union_tag_t>(frame->offset);
290                 if (union_tag >= frame->union_state.type_count) {
291                     SetError("Tried to decode a bad union discriminant");
292                     FIDL_POP_AND_CONTINUE_OR_RETURN;
293                 }
294                 const fidl_type_t* member = frame->union_state.types[union_tag];
295                 if (!member) {
296                     Pop();
297                     continue;
298                 }
299                 frame->offset += frame->union_state.data_offset;
300                 *frame = Frame(member, frame->offset);
301                 continue;
302             }
303             case Frame::kStateUnionPointer: {
304                 auto union_ptr_ptr = TypedAt<fidl_union_tag_t*>(frame->offset);
305                 switch (GetPointerState(TypedAt<void>(frame->offset))) {
306                 case PointerState::PRESENT:
307                     break;
308                 case PointerState::ABSENT:
309                     Pop();
310                     continue;
311                 default:
312                     SetError("Tried to decode a bad union pointer");
313                     FIDL_POP_AND_CONTINUE_OR_RETURN;
314                 }
315                 if (!ClaimOutOfLineStorage(frame->union_pointer_state.union_type->size, *union_ptr_ptr,
316                                            &frame->offset)) {
317                     SetError("message wanted to store too large of a nullable union");
318                     FIDL_POP_AND_CONTINUE_OR_RETURN;
319                 }
320                 UpdatePointer(union_ptr_ptr, TypedAt<fidl_union_tag_t>(frame->offset));
321                 const fidl::FidlCodedUnion* coded_union = frame->union_pointer_state.union_type;
322                 *frame = Frame(coded_union, frame->offset);
323                 continue;
324             }
325             case Frame::kStateArray: {
326                 const uint32_t element_offset = frame->NextArrayOffset();
327                 if (element_offset == frame->array_state.array_size) {
328                     Pop();
329                     continue;
330                 }
331                 const fidl_type_t* element_type = frame->array_state.element;
332                 const uint32_t offset = frame->offset + element_offset;
333                 if (!Push(Frame(element_type, offset))) {
334                     SetError("recursion depth exceeded decoding array");
335                     FIDL_POP_AND_CONTINUE_OR_RETURN;
336                 }
337                 continue;
338             }
339             case Frame::kStateString: {
340                 auto string_ptr = TypedAt<fidl_string_t>(frame->offset);
341                 // The string storage may be Absent for nullable strings and must
342                 // otherwise be Present. No other values are allowed.
343                 switch (GetPointerState(&string_ptr->data)) {
344                 case PointerState::PRESENT:
345                     break;
346                 case PointerState::ABSENT:
347                     if (!frame->string_state.nullable) {
348                         SetError("message tried to decode an absent non-nullable string");
349                         FIDL_POP_AND_CONTINUE_OR_RETURN;
350                     }
351                     if (string_ptr->size != 0u) {
352                         SetError("message tried to decode an absent string of non-zero length");
353                         FIDL_POP_AND_CONTINUE_OR_RETURN;
354                     }
355                     Pop();
356                     continue;
357                 default:
358                     SetError(
359                         "message tried to decode a string that is neither present nor absent");
360                     FIDL_POP_AND_CONTINUE_OR_RETURN;
361                 }
362                 uint64_t bound = frame->string_state.max_size;
363                 uint64_t size = string_ptr->size;
364                 if (size > bound) {
365                     SetError("message tried to decode too large of a bounded string");
366                     FIDL_POP_AND_CONTINUE_OR_RETURN;
367                 }
368                 uint32_t string_data_offset = 0u;
369                 if (!ClaimOutOfLineStorage(static_cast<uint32_t>(size), string_ptr->data, &string_data_offset)) {
370                     SetError("decoding a string overflowed buffer");
371                     FIDL_POP_AND_CONTINUE_OR_RETURN;
372                 }
373                 UpdatePointer(&string_ptr->data, TypedAt<char>(string_data_offset));
374                 Pop();
375                 continue;
376             }
377             case Frame::kStateHandle: {
378                 auto handle_ptr = TypedAt<zx_handle_t>(frame->offset);
379                 // The handle storage may be Absent for nullable handles and must
380                 // otherwise be Present. No other values are allowed.
381                 switch (GetHandleState(*handle_ptr)) {
382                 case HandleState::ABSENT:
383                     if (frame->handle_state.nullable) {
384                         Pop();
385                         continue;
386                     }
387                     SetError("message tried to decode a non-present handle");
388                     FIDL_POP_AND_CONTINUE_OR_RETURN;
389                 case HandleState::PRESENT:
390                     if (!ClaimHandle(handle_ptr)) {
391                         SetError("message decoded too many handles");
392                         FIDL_POP_AND_CONTINUE_OR_RETURN;
393                     }
394                     Pop();
395                     continue;
396                 default:
397                     // The value at the handle was garbage.
398                     SetError("message tried to decode a garbage handle");
399                     FIDL_POP_AND_CONTINUE_OR_RETURN;
400                 }
401             }
402             case Frame::kStateVector: {
403                 auto vector_ptr = TypedAt<fidl_vector_t>(frame->offset);
404                 // The vector storage may be Absent for nullable vectors and must
405                 // otherwise be Present. No other values are allowed.
406                 switch (GetPointerState(&vector_ptr->data)) {
407                 case PointerState::PRESENT:
408                     break;
409                 case PointerState::ABSENT:
410                     if (!frame->vector_state.nullable) {
411                         SetError("message tried to decode an absent non-nullable vector");
412                         FIDL_POP_AND_CONTINUE_OR_RETURN;
413                     }
414                     if (vector_ptr->count != 0u) {
415                         SetError("message tried to decode an absent vector of non-zero elements");
416                         FIDL_POP_AND_CONTINUE_OR_RETURN;
417                     }
418                     Pop();
419                     continue;
420                 default:
421                     SetError("message tried to decode a non-present vector");
422                     FIDL_POP_AND_CONTINUE_OR_RETURN;
423                 }
424                 if (vector_ptr->count > frame->vector_state.max_count) {
425                     SetError("message tried to decode too large of a bounded vector");
426                     FIDL_POP_AND_CONTINUE_OR_RETURN;
427                 }
428                 uint32_t size;
429                 if (mul_overflow(vector_ptr->count, frame->vector_state.element_size, &size)) {
430                     SetError("integer overflow calculating vector size");
431                     FIDL_POP_AND_CONTINUE_OR_RETURN;
432                 }
433                 if (!ClaimOutOfLineStorage(size, vector_ptr->data, &frame->offset)) {
434                     SetError("message wanted to store too large of a vector");
435                     FIDL_POP_AND_CONTINUE_OR_RETURN;
436                 }
437                 UpdatePointer(&vector_ptr->data, TypedAt<void>(frame->offset));
438                 if (frame->vector_state.element) {
439                     // Continue by decoding the vector elements as an array.
440                     *frame = Frame(frame->vector_state.element, size,
441                                    frame->vector_state.element_size, frame->offset);
442                 } else {
443                     // If there is no element type pointer, there is
444                     // nothing to decode in the vector secondary
445                     // payload. So just continue.
446                     Pop();
447                 }
448                 continue;
449             }
450             case Frame::kStateDone: {
451                 if (out_of_line_offset_ != num_bytes()) {
452                     SetError("message did not decode all provided bytes");
453                 }
454                 return;
455             }
456             }
457         }
458 
459 #undef FIDL_POP_AND_CONTINUE_OR_RETURN
460     }
461 
462 protected:
463     void SetError(const char* error_msg) {
464         derived()->SetError(error_msg);
465     }
466 
467     template <typename T>
468     typename SetPtrConst<!kMutating, T>::type TypedAt(uint32_t offset) const {
469         return reinterpret_cast<typename SetPtrConst<!kMutating, T>::type>(bytes() + offset);
470     }
471 
472     enum class PointerState : uintptr_t {
473         PRESENT = FIDL_ALLOC_PRESENT,
474         ABSENT = FIDL_ALLOC_ABSENT,
475         INVALID = 1 // *OR* *ANY* non PRESENT/ABSENT value.
476     };
477 
478     enum class HandleState : zx_handle_t {
479         PRESENT = FIDL_HANDLE_PRESENT,
480         ABSENT = FIDL_HANDLE_ABSENT,
481         INVALID = 1 // *OR* *ANY* non PRESENT/ABSENT value.
482     };
483 
484     uint32_t handle_idx() const { return handle_idx_; }
485 
486 private:
487     Derived* derived() {
488         return static_cast<Derived*>(this);
489     }
490 
491     const Derived* derived() const {
492         return static_cast<const Derived*>(this);
493     }
494 
495     // Returns a pointer to the bytes in the message.
496     auto bytes() const {
497         return derived()->bytes();
498     }
499 
500     // Returns the number of bytes in the message.
501     auto num_bytes() const {
502         return derived()->num_bytes();
503     }
504 
505     // Returns the number of handles in the message (encoding: the max number of handles in the message).
506     auto num_handles() const {
507         return derived()->num_handles();
508     }
509 
510     // Returns PRESENT/ABSENT/INVALID for a given pointer value.
511     PointerState GetPointerState(const void* ptr) const {
512         return derived()->GetPointerState(ptr);
513     }
514 
515     // Returns PRESENT/ABSENT/INVALID for a given handle value.
516     HandleState GetHandleState(zx_handle_t p) const {
517         return derived()->GetHandleState(p);
518     }
519 
520     // If required: mutate a pointer to the dual representation.
521     template <class T2, class T1>
522     void UpdatePointer(T2 p, T1 v) {
523         derived()->UpdatePointer(p, v);
524     }
525 
526     // Returns true when a handle was claimed, and false when the
527     // handles are exhausted.
528     template <class ZxHandleTPointer>
529     bool ClaimHandle(ZxHandleTPointer out_handle) {
530         if (handle_idx_ == num_handles()) {
531             derived()->UnclaimedHandle(out_handle);
532             return false;
533         }
534         derived()->ClaimedHandle(out_handle, handle_idx_);
535         ++handle_idx_;
536         return true;
537     }
538 
539     // Returns true when the buffer space is claimed, and false when
540     // the requested claim is too large for bytes_.
541     bool ClaimOutOfLineStorage(uint32_t size, const void* storage, uint32_t* out_offset) {
542         if (!derived()->ValidateOutOfLineStorageClaim(storage, &bytes()[out_of_line_offset_])) {
543             return false;
544         }
545 
546         // We have to manually maintain alignment here. For example, a pointer
547         // to a struct that is 4 bytes still needs to advance the next
548         // out-of-line offset by 8 to maintain the aligned-to-FIDL_ALIGNMENT
549         // property.
550         static constexpr uint32_t mask = FIDL_ALIGNMENT - 1;
551         uint32_t offset = out_of_line_offset_;
552         if (add_overflow(offset, size, &offset) ||
553             add_overflow(offset, mask, &offset)) {
554             return false;
555         }
556         offset &= ~mask;
557 
558         if (offset > num_bytes()) {
559             return false;
560         }
561         *out_offset = out_of_line_offset_;
562         out_of_line_offset_ = offset;
563         return true;
564     }
565 
566     uint32_t TypeSize(const fidl_type_t* type) {
567         switch (type->type_tag) {
568         case fidl::kFidlTypeStructPointer:
569         case fidl::kFidlTypeTablePointer:
570         case fidl::kFidlTypeUnionPointer:
571             return sizeof(uint64_t);
572         case fidl::kFidlTypeHandle:
573             return sizeof(zx_handle_t);
574         case fidl::kFidlTypeStruct:
575             return type->coded_struct.size;
576         case fidl::kFidlTypeTable:
577             return sizeof(fidl_vector_t);
578         case fidl::kFidlTypeUnion:
579             return type->coded_union.size;
580         case fidl::kFidlTypeString:
581             return sizeof(fidl_string_t);
582         case fidl::kFidlTypeArray:
583             return type->coded_array.array_size;
584         case fidl::kFidlTypeVector:
585             return sizeof(fidl_vector_t);
586         }
587         abort();
588         return 0;
589     }
590 
591     // Functions that manipulate the decoding stack frames.
592     struct Frame {
593         Frame(const fidl_type_t* fidl_type, uint32_t offset)
594             : offset(offset) {
595             switch (fidl_type->type_tag) {
596             case fidl::kFidlTypeStruct:
597                 state = kStateStruct;
598                 struct_state.fields = fidl_type->coded_struct.fields;
599                 struct_state.field_count = fidl_type->coded_struct.field_count;
600                 break;
601             case fidl::kFidlTypeStructPointer:
602                 state = kStateStructPointer;
603                 struct_pointer_state.struct_type = fidl_type->coded_struct_pointer.struct_type;
604                 break;
605             case fidl::kFidlTypeTable:
606                 state = kStateTable;
607                 table_state.fields = fidl_type->coded_table.fields;
608                 table_state.field_count = fidl_type->coded_table.field_count;
609                 table_state.present_count = 0;
610                 break;
611             case fidl::kFidlTypeTablePointer:
612                 state = kStateTablePointer;
613                 table_pointer_state.table_type = fidl_type->coded_table_pointer.table_type;
614                 break;
615             case fidl::kFidlTypeUnion:
616                 state = kStateUnion;
617                 union_state.types = fidl_type->coded_union.types;
618                 union_state.type_count = fidl_type->coded_union.type_count;
619                 union_state.data_offset = fidl_type->coded_union.data_offset;
620                 break;
621             case fidl::kFidlTypeUnionPointer:
622                 state = kStateUnionPointer;
623                 union_pointer_state.union_type = fidl_type->coded_union_pointer.union_type;
624                 break;
625             case fidl::kFidlTypeArray:
626                 state = kStateArray;
627                 array_state.element = fidl_type->coded_array.element;
628                 array_state.array_size = fidl_type->coded_array.array_size;
629                 array_state.element_size = fidl_type->coded_array.element_size;
630                 break;
631             case fidl::kFidlTypeString:
632                 state = kStateString;
633                 string_state.max_size = fidl_type->coded_string.max_size;
634                 string_state.nullable = fidl_type->coded_string.nullable;
635                 break;
636             case fidl::kFidlTypeHandle:
637                 state = kStateHandle;
638                 handle_state.nullable = fidl_type->coded_handle.nullable;
639                 break;
640             case fidl::kFidlTypeVector:
641                 state = kStateVector;
642                 vector_state.element = fidl_type->coded_vector.element;
643                 vector_state.max_count = fidl_type->coded_vector.max_count;
644                 vector_state.element_size = fidl_type->coded_vector.element_size;
645                 vector_state.nullable = fidl_type->coded_vector.nullable;
646                 break;
647             }
648         }
649 
650         Frame(const fidl::FidlCodedStruct* coded_struct, uint32_t offset)
651             : offset(offset) {
652             state = kStateStruct;
653             struct_state.fields = coded_struct->fields;
654             struct_state.field_count = coded_struct->field_count;
655         }
656 
657         Frame(const fidl::FidlCodedTable* coded_table, uint32_t offset)
658             : offset(offset) {
659             state = kStateStruct;
660             table_state.fields = coded_table->fields;
661             table_state.field_count = coded_table->field_count;
662         }
663 
664         Frame(const fidl::FidlCodedUnion* coded_union, uint32_t offset)
665             : offset(offset) {
666             state = kStateUnion;
667             union_state.types = coded_union->types;
668             union_state.type_count = coded_union->type_count;
669             union_state.data_offset = coded_union->data_offset;
670         }
671 
672         Frame(const fidl_type_t* element, uint32_t array_size, uint32_t element_size,
673               uint32_t offset)
674             : offset(offset) {
675             state = kStateArray;
676             array_state.element = element;
677             array_state.array_size = array_size;
678             array_state.element_size = element_size;
679         }
680 
681         // The default constructor does nothing when initializing the stack of frames.
682         Frame() {}
683 
684         static Frame DoneSentinel() {
685             Frame frame;
686             frame.state = kStateDone;
687             return frame;
688         }
689 
690         uint32_t NextStructField() {
691             ZX_DEBUG_ASSERT(state == kStateStruct);
692 
693             uint32_t current = field;
694             field += 1;
695             return current;
696         }
697 
698         uint32_t NextArrayOffset() {
699             ZX_DEBUG_ASSERT(state == kStateArray);
700 
701             uint32_t current = field;
702             field += array_state.element_size;
703             return current;
704         }
705 
706         enum : int {
707             kStateStruct,
708             kStateStructPointer,
709             kStateTable,
710             kStateTablePointer,
711             kStateUnion,
712             kStateUnionPointer,
713             kStateArray,
714             kStateString,
715             kStateHandle,
716             kStateVector,
717 
718             kStateDone,
719         } state;
720         // A byte offset into bytes_;
721         uint32_t offset;
722 
723         // This is a subset of the information recorded in the
724         // fidl_type structures needed for decoding state. For
725         // example, struct sizes do not need to be present here.
726         union {
727             struct {
728                 const fidl::FidlField* fields;
729                 uint32_t field_count;
730             } struct_state;
731             struct {
732                 const fidl::FidlCodedStruct* struct_type;
733             } struct_pointer_state;
734             struct {
735                 const fidl::FidlTableField* fields;
736                 uint32_t known_index;
737                 uint32_t field_count;
738                 uint32_t present_count;
739                 uint32_t end_offset;
740                 uint32_t end_handle;
741             } table_state;
742             struct {
743                 const fidl::FidlCodedTable* table_type;
744             } table_pointer_state;
745             struct {
746                 const fidl_type_t* const* types;
747                 uint32_t type_count;
748                 uint32_t data_offset;
749             } union_state;
750             struct {
751                 const fidl::FidlCodedUnion* union_type;
752             } union_pointer_state;
753             struct {
754                 const fidl_type_t* element;
755                 uint32_t array_size;
756                 uint32_t element_size;
757             } array_state;
758             struct {
759                 uint32_t max_size;
760                 bool nullable;
761             } string_state;
762             struct {
763                 bool nullable;
764             } handle_state;
765             struct {
766                 const fidl_type* element;
767                 uint32_t max_count;
768                 uint32_t element_size;
769                 bool nullable;
770             } vector_state;
771         };
772 
773         uint32_t field = 0u;
774     };
775 
776     // Returns true on success and false on recursion overflow.
777     bool Push(Frame frame) {
778         if (depth_ == FIDL_RECURSION_DEPTH) {
779             return false;
780         }
781         decoding_frames_[depth_] = frame;
782         ++depth_;
783         return true;
784     }
785 
786     void Pop() {
787         ZX_DEBUG_ASSERT(depth_ != 0u);
788         --depth_;
789     }
790 
791     Frame* Peek() {
792         ZX_DEBUG_ASSERT(depth_ != 0u);
793         return &decoding_frames_[depth_ - 1];
794     }
795 
796     // Message state passed in to the constructor.
797     const fidl_type_t* const type_;
798 
799     // Internal state.
800     uint32_t handle_idx_ = 0u;
801     uint32_t out_of_line_offset_ = 0u;
802 
803     // Decoding stack state.
804     uint32_t depth_ = 0u;
805     Frame decoding_frames_[FIDL_RECURSION_DEPTH];
806 };
807 
808 } // namespace internal
809 } // namespace fidl
810