blob: 92cb2e1df82582711f82384b0d625146c5f99565 [file] [log] [blame]
Doug Horn1427b6a2018-12-11 13:19:16 -08001// 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
7namespace fidl {
8namespace internal {
9
10// Some assumptions about data type layout.
11static_assert(offsetof(fidl_string_t, size) == 0u, "");
12static_assert(offsetof(fidl_string_t, data) == 8u, "");
13
14static_assert(offsetof(fidl_vector_t, count) == 0u, "");
15static_assert(offsetof(fidl_vector_t, data) == 8u, "");
16
17static_assert(ZX_HANDLE_INVALID == FIDL_HANDLE_ABSENT, "");
18
19template <bool kConst, class U>
20struct SetPtrConst;
21
22template <class U>
23struct SetPtrConst<false, U> {
24 typedef U* type;
25};
26
27template <class U>
28struct 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
53template <class Derived, bool kMutating, bool kContinueAfterErrors>
54class BufferWalker {
55public:
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
462protected:
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
486private:
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