| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #pragma once |
| |
| namespace fidl { |
| namespace internal { |
| |
| // Some assumptions about data type layout. |
| static_assert(offsetof(fidl_string_t, size) == 0u, ""); |
| static_assert(offsetof(fidl_string_t, data) == 8u, ""); |
| |
| static_assert(offsetof(fidl_vector_t, count) == 0u, ""); |
| static_assert(offsetof(fidl_vector_t, data) == 8u, ""); |
| |
| static_assert(ZX_HANDLE_INVALID == FIDL_HANDLE_ABSENT, ""); |
| |
| template <bool kConst, class U> |
| struct SetPtrConst; |
| |
| template <class U> |
| struct SetPtrConst<false, U> { |
| typedef U* type; |
| }; |
| |
| template <class U> |
| struct SetPtrConst<true, U> { |
| typedef const U* type; |
| }; |
| |
| // Walks over a FIDL buffer and validates/encodes/decodes it per-Derived. |
| // |
| // kMutating controls whether this deals with mutable bytes or immutable bytes |
| // (validation wants immutable, encode/decode wants mutable) |
| // |
| // kContinueAfterErrors controls whether parsing is continued upon failure (encode needs this to |
| // see all available handles). |
| // |
| // Derived should offer the following methods: |
| // |
| // const? uint8_t* bytes() - returns the start of the buffer of bytes |
| // uint32_t num_bytes() - returns the number of bytes in said buffer |
| // uint32_t num_handles() - returns the number of handles that are claimable |
| // bool ValidateOutOfLineStorageClaim(const void* a, const void* b) |
| // - returns true if a legally points to b |
| // void UnclaimedHandle(zx_handle_t*) - notes that a handle was skipped |
| // void ClaimedHandle(zx_handle_t*, uint32_t idx) - notes that a handle was claimed |
| // PointerState GetPointerState(const void* ptr) - returns whether a pointer is present or not |
| // HandleState GetHandleState(zx_handle_t) - returns if a handle is present or not |
| // void UpdatePointer(T**p, T*v) - mutates a pointer representation for a present pointer |
| // void SetError(const char* error_msg) - flags that an error occurred |
| template <class Derived, bool kMutating, bool kContinueAfterErrors> |
| class BufferWalker { |
| public: |
| explicit BufferWalker(const fidl_type* type) |
| : type_(type) {} |
| |
| void Walk() { |
| // The first decode is special. It must be a struct or a table. |
| // We need to know the size of the first element to compute the start of |
| // the out-of-line allocations. |
| |
| if (type_ == nullptr) { |
| SetError("Cannot decode a null fidl type"); |
| return; |
| } |
| |
| if (bytes() == nullptr) { |
| SetError("Cannot decode null bytes"); |
| return; |
| } |
| |
| switch (type_->type_tag) { |
| case fidl::kFidlTypeStruct: |
| if (num_bytes() < type_->coded_struct.size) { |
| SetError("Message size is smaller than expected"); |
| return; |
| } |
| out_of_line_offset_ = static_cast<uint32_t>(fidl::FidlAlign(type_->coded_struct.size)); |
| break; |
| case fidl::kFidlTypeTable: |
| if (num_bytes() < sizeof(fidl_vector_t)) { |
| SetError("Message size is smaller than expected"); |
| return; |
| } |
| out_of_line_offset_ = static_cast<uint32_t>(fidl::FidlAlign(sizeof(fidl_vector_t))); |
| break; |
| default: |
| SetError("Message must be a struct or a table"); |
| return; |
| } |
| |
| Push(Frame::DoneSentinel()); |
| Push(Frame(type_, 0u)); |
| |
| // Macro to insert the relevant goop required to support two control flows here: |
| // one where we keep reading after error, and another where we return immediately. |
| // No runtime overhead thanks to if constexpr magic. |
| #define FIDL_POP_AND_CONTINUE_OR_RETURN \ |
| if (kContinueAfterErrors) { \ |
| Pop(); \ |
| continue; \ |
| } else { \ |
| return; \ |
| } |
| |
| for (;;) { |
| Frame* frame = Peek(); |
| |
| switch (frame->state) { |
| case Frame::kStateStruct: { |
| const uint32_t field_index = frame->NextStructField(); |
| if (field_index == frame->struct_state.field_count) { |
| Pop(); |
| continue; |
| } |
| const fidl::FidlField& field = frame->struct_state.fields[field_index]; |
| const fidl_type_t* field_type = field.type; |
| const uint32_t field_offset = frame->offset + field.offset; |
| if (!Push(Frame(field_type, field_offset))) { |
| SetError("recursion depth exceeded processing struct"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| continue; |
| } |
| case Frame::kStateStructPointer: { |
| switch (GetPointerState(TypedAt<void>(frame->offset))) { |
| case PointerState::PRESENT: |
| break; |
| case PointerState::ABSENT: |
| Pop(); |
| continue; |
| default: |
| SetError("Tried to decode a bad struct pointer"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| auto struct_ptr_ptr = TypedAt<void*>(frame->offset); |
| if (!ClaimOutOfLineStorage(frame->struct_pointer_state.struct_type->size, |
| *struct_ptr_ptr, &frame->offset)) { |
| SetError("message wanted to store too large of a nullable struct"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| UpdatePointer(struct_ptr_ptr, TypedAt<void>(frame->offset)); |
| const fidl::FidlCodedStruct* coded_struct = frame->struct_pointer_state.struct_type; |
| *frame = Frame(coded_struct, frame->offset); |
| continue; |
| } |
| case Frame::kStateTable: { |
| if (frame->field == 0u) { |
| auto envelope_vector_ptr = TypedAt<fidl_vector_t>(frame->offset); |
| switch (GetPointerState(&envelope_vector_ptr->data)) { |
| case PointerState::PRESENT: |
| break; |
| case PointerState::ABSENT: |
| SetError("Table data cannot be absent"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| default: |
| SetError("message tried to decode a non-present vector"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| uint32_t size; |
| if (mul_overflow(envelope_vector_ptr->count, 2 * sizeof(uint64_t), &size)) { |
| SetError("integer overflow calculating table size"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| if (!ClaimOutOfLineStorage(size, envelope_vector_ptr->data, &frame->offset)) { |
| SetError("message wanted to store too large of a table"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| UpdatePointer(&envelope_vector_ptr->data, TypedAt<void>(frame->offset)); |
| frame->field = 1; |
| frame->table_state.known_index = 0; |
| frame->table_state.present_count = static_cast<uint32_t>(envelope_vector_ptr->count); |
| frame->table_state.end_offset = out_of_line_offset_; |
| frame->table_state.end_handle = handle_idx_; |
| continue; |
| } |
| if (frame->table_state.end_offset != out_of_line_offset_) { |
| SetError("Table field was mis-sized"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| if (frame->table_state.end_handle != handle_idx_) { |
| SetError("Table handles were mis-sized"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| if (frame->field > frame->table_state.present_count) { |
| Pop(); |
| continue; |
| } |
| const fidl::FidlTableField* known_field = nullptr; |
| if (frame->table_state.known_index < frame->table_state.field_count) { |
| const fidl::FidlTableField* field = |
| &frame->table_state.fields[frame->table_state.known_index]; |
| if (field->ordinal == frame->field) { |
| known_field = field; |
| frame->table_state.known_index++; |
| } |
| } |
| const uint32_t tag_offset = static_cast<uint32_t>( |
| frame->offset + (frame->field - 1) * 2 * sizeof(uint64_t)); |
| const uint32_t data_offset = static_cast<uint32_t>( |
| tag_offset + sizeof(uint64_t)); |
| const uint64_t packed_sizes = *TypedAt<uint64_t>(tag_offset); |
| frame->field++; |
| switch (GetPointerState(TypedAt<void>(data_offset))) { |
| case PointerState::PRESENT: |
| if (packed_sizes != 0) |
| break; // expected |
| |
| SetError("Table envelope has present data pointer, but no data, and no handles"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| case PointerState::ABSENT: |
| if (packed_sizes == 0) |
| continue; // skip |
| |
| SetError("Table envelope has absent data pointer, yet has data and/or handles"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| default: |
| SetError("Table envelope has bad data pointer"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| uint32_t offset; |
| uint32_t handles; |
| const uint32_t table_bytes = static_cast<uint32_t>(packed_sizes & 0xffffffffu); |
| const uint32_t table_handles = static_cast<uint32_t>(packed_sizes >> 32); |
| if (add_overflow(out_of_line_offset_, table_bytes, &offset) || offset > num_bytes()) { |
| SetError("integer overflow decoding table field"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| if (add_overflow(handle_idx_, table_handles, &handles) || |
| handles > num_handles()) { |
| SetError("integer overflow decoding table handles"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| frame->table_state.end_offset = offset; |
| frame->table_state.end_handle = handles; |
| if (known_field != nullptr) { |
| const fidl_type_t* field_type = known_field->type; |
| uint32_t field_offset; |
| if (!ClaimOutOfLineStorage(TypeSize(field_type), TypedAt<void*>(data_offset), &field_offset)) { |
| SetError("table wanted too many bytes in field"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| UpdatePointer(TypedAt<void*>(data_offset), TypedAt<void>(field_offset)); |
| if (!Push(Frame(field_type, field_offset))) { |
| SetError("recursion depth exceeded decoding table"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| } else { |
| // Table data will not be processed: discard it. |
| uint32_t field_offset; |
| if (!ClaimOutOfLineStorage(table_bytes, TypedAt<void*>(data_offset), &field_offset)) { |
| SetError("table wanted too many bytes in field"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| UpdatePointer(TypedAt<void*>(data_offset), TypedAt<void>(field_offset)); |
| for (uint32_t i = 0; i < table_handles; i++) { |
| if (!ClaimHandle(nullptr)) { |
| SetError("expected handle not present"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| } |
| } |
| continue; |
| } |
| case Frame::kStateTablePointer: { |
| switch (GetPointerState(TypedAt<void>(frame->offset))) { |
| case PointerState::PRESENT: |
| break; |
| case PointerState::ABSENT: |
| Pop(); |
| continue; |
| default: |
| SetError("Tried to decode a bad table pointer"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| auto table_ptr_ptr = TypedAt<void*>(frame->offset); |
| if (!ClaimOutOfLineStorage(sizeof(fidl_vector_t), *table_ptr_ptr, &frame->offset)) { |
| SetError("message wanted to store too large of a nullable table"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| UpdatePointer(table_ptr_ptr, TypedAt<void>(frame->offset)); |
| const fidl::FidlCodedTable* coded_table = frame->table_pointer_state.table_type; |
| *frame = Frame(coded_table, frame->offset); |
| continue; |
| } |
| case Frame::kStateUnion: { |
| fidl_union_tag_t union_tag = *TypedAt<fidl_union_tag_t>(frame->offset); |
| if (union_tag >= frame->union_state.type_count) { |
| SetError("Tried to decode a bad union discriminant"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| const fidl_type_t* member = frame->union_state.types[union_tag]; |
| if (!member) { |
| Pop(); |
| continue; |
| } |
| frame->offset += frame->union_state.data_offset; |
| *frame = Frame(member, frame->offset); |
| continue; |
| } |
| case Frame::kStateUnionPointer: { |
| auto union_ptr_ptr = TypedAt<fidl_union_tag_t*>(frame->offset); |
| switch (GetPointerState(TypedAt<void>(frame->offset))) { |
| case PointerState::PRESENT: |
| break; |
| case PointerState::ABSENT: |
| Pop(); |
| continue; |
| default: |
| SetError("Tried to decode a bad union pointer"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| if (!ClaimOutOfLineStorage(frame->union_pointer_state.union_type->size, *union_ptr_ptr, |
| &frame->offset)) { |
| SetError("message wanted to store too large of a nullable union"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| UpdatePointer(union_ptr_ptr, TypedAt<fidl_union_tag_t>(frame->offset)); |
| const fidl::FidlCodedUnion* coded_union = frame->union_pointer_state.union_type; |
| *frame = Frame(coded_union, frame->offset); |
| continue; |
| } |
| case Frame::kStateArray: { |
| const uint32_t element_offset = frame->NextArrayOffset(); |
| if (element_offset == frame->array_state.array_size) { |
| Pop(); |
| continue; |
| } |
| const fidl_type_t* element_type = frame->array_state.element; |
| const uint32_t offset = frame->offset + element_offset; |
| if (!Push(Frame(element_type, offset))) { |
| SetError("recursion depth exceeded decoding array"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| continue; |
| } |
| case Frame::kStateString: { |
| auto string_ptr = TypedAt<fidl_string_t>(frame->offset); |
| // The string storage may be Absent for nullable strings and must |
| // otherwise be Present. No other values are allowed. |
| switch (GetPointerState(&string_ptr->data)) { |
| case PointerState::PRESENT: |
| break; |
| case PointerState::ABSENT: |
| if (!frame->string_state.nullable) { |
| SetError("message tried to decode an absent non-nullable string"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| if (string_ptr->size != 0u) { |
| SetError("message tried to decode an absent string of non-zero length"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| Pop(); |
| continue; |
| default: |
| SetError( |
| "message tried to decode a string that is neither present nor absent"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| uint64_t bound = frame->string_state.max_size; |
| uint64_t size = string_ptr->size; |
| if (size > bound) { |
| SetError("message tried to decode too large of a bounded string"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| uint32_t string_data_offset = 0u; |
| if (!ClaimOutOfLineStorage(static_cast<uint32_t>(size), string_ptr->data, &string_data_offset)) { |
| SetError("decoding a string overflowed buffer"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| UpdatePointer(&string_ptr->data, TypedAt<char>(string_data_offset)); |
| Pop(); |
| continue; |
| } |
| case Frame::kStateHandle: { |
| auto handle_ptr = TypedAt<zx_handle_t>(frame->offset); |
| // The handle storage may be Absent for nullable handles and must |
| // otherwise be Present. No other values are allowed. |
| switch (GetHandleState(*handle_ptr)) { |
| case HandleState::ABSENT: |
| if (frame->handle_state.nullable) { |
| Pop(); |
| continue; |
| } |
| SetError("message tried to decode a non-present handle"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| case HandleState::PRESENT: |
| if (!ClaimHandle(handle_ptr)) { |
| SetError("message decoded too many handles"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| Pop(); |
| continue; |
| default: |
| // The value at the handle was garbage. |
| SetError("message tried to decode a garbage handle"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| } |
| case Frame::kStateVector: { |
| auto vector_ptr = TypedAt<fidl_vector_t>(frame->offset); |
| // The vector storage may be Absent for nullable vectors and must |
| // otherwise be Present. No other values are allowed. |
| switch (GetPointerState(&vector_ptr->data)) { |
| case PointerState::PRESENT: |
| break; |
| case PointerState::ABSENT: |
| if (!frame->vector_state.nullable) { |
| SetError("message tried to decode an absent non-nullable vector"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| if (vector_ptr->count != 0u) { |
| SetError("message tried to decode an absent vector of non-zero elements"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| Pop(); |
| continue; |
| default: |
| SetError("message tried to decode a non-present vector"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| if (vector_ptr->count > frame->vector_state.max_count) { |
| SetError("message tried to decode too large of a bounded vector"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| uint32_t size; |
| if (mul_overflow(vector_ptr->count, frame->vector_state.element_size, &size)) { |
| SetError("integer overflow calculating vector size"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| if (!ClaimOutOfLineStorage(size, vector_ptr->data, &frame->offset)) { |
| SetError("message wanted to store too large of a vector"); |
| FIDL_POP_AND_CONTINUE_OR_RETURN; |
| } |
| UpdatePointer(&vector_ptr->data, TypedAt<void>(frame->offset)); |
| if (frame->vector_state.element) { |
| // Continue by decoding the vector elements as an array. |
| *frame = Frame(frame->vector_state.element, size, |
| frame->vector_state.element_size, frame->offset); |
| } else { |
| // If there is no element type pointer, there is |
| // nothing to decode in the vector secondary |
| // payload. So just continue. |
| Pop(); |
| } |
| continue; |
| } |
| case Frame::kStateDone: { |
| if (out_of_line_offset_ != num_bytes()) { |
| SetError("message did not decode all provided bytes"); |
| } |
| return; |
| } |
| } |
| } |
| |
| #undef FIDL_POP_AND_CONTINUE_OR_RETURN |
| } |
| |
| protected: |
| void SetError(const char* error_msg) { |
| derived()->SetError(error_msg); |
| } |
| |
| template <typename T> |
| typename SetPtrConst<!kMutating, T>::type TypedAt(uint32_t offset) const { |
| return reinterpret_cast<typename SetPtrConst<!kMutating, T>::type>(bytes() + offset); |
| } |
| |
| enum class PointerState : uintptr_t { |
| PRESENT = FIDL_ALLOC_PRESENT, |
| ABSENT = FIDL_ALLOC_ABSENT, |
| INVALID = 1 // *OR* *ANY* non PRESENT/ABSENT value. |
| }; |
| |
| enum class HandleState : zx_handle_t { |
| PRESENT = FIDL_HANDLE_PRESENT, |
| ABSENT = FIDL_HANDLE_ABSENT, |
| INVALID = 1 // *OR* *ANY* non PRESENT/ABSENT value. |
| }; |
| |
| uint32_t handle_idx() const { return handle_idx_; } |
| |
| private: |
| Derived* derived() { |
| return static_cast<Derived*>(this); |
| } |
| |
| const Derived* derived() const { |
| return static_cast<const Derived*>(this); |
| } |
| |
| // Returns a pointer to the bytes in the message. |
| auto bytes() const { |
| return derived()->bytes(); |
| } |
| |
| // Returns the number of bytes in the message. |
| auto num_bytes() const { |
| return derived()->num_bytes(); |
| } |
| |
| // Returns the number of handles in the message (encoding: the max number of handles in the message). |
| auto num_handles() const { |
| return derived()->num_handles(); |
| } |
| |
| // Returns PRESENT/ABSENT/INVALID for a given pointer value. |
| PointerState GetPointerState(const void* ptr) const { |
| return derived()->GetPointerState(ptr); |
| } |
| |
| // Returns PRESENT/ABSENT/INVALID for a given handle value. |
| HandleState GetHandleState(zx_handle_t p) const { |
| return derived()->GetHandleState(p); |
| } |
| |
| // If required: mutate a pointer to the dual representation. |
| template <class T2, class T1> |
| void UpdatePointer(T2 p, T1 v) { |
| derived()->UpdatePointer(p, v); |
| } |
| |
| // Returns true when a handle was claimed, and false when the |
| // handles are exhausted. |
| template <class ZxHandleTPointer> |
| bool ClaimHandle(ZxHandleTPointer out_handle) { |
| if (handle_idx_ == num_handles()) { |
| derived()->UnclaimedHandle(out_handle); |
| return false; |
| } |
| derived()->ClaimedHandle(out_handle, handle_idx_); |
| ++handle_idx_; |
| return true; |
| } |
| |
| // Returns true when the buffer space is claimed, and false when |
| // the requested claim is too large for bytes_. |
| bool ClaimOutOfLineStorage(uint32_t size, const void* storage, uint32_t* out_offset) { |
| if (!derived()->ValidateOutOfLineStorageClaim(storage, &bytes()[out_of_line_offset_])) { |
| return false; |
| } |
| |
| // We have to manually maintain alignment here. For example, a pointer |
| // to a struct that is 4 bytes still needs to advance the next |
| // out-of-line offset by 8 to maintain the aligned-to-FIDL_ALIGNMENT |
| // property. |
| static constexpr uint32_t mask = FIDL_ALIGNMENT - 1; |
| uint32_t offset = out_of_line_offset_; |
| if (add_overflow(offset, size, &offset) || |
| add_overflow(offset, mask, &offset)) { |
| return false; |
| } |
| offset &= ~mask; |
| |
| if (offset > num_bytes()) { |
| return false; |
| } |
| *out_offset = out_of_line_offset_; |
| out_of_line_offset_ = offset; |
| return true; |
| } |
| |
| uint32_t TypeSize(const fidl_type_t* type) { |
| switch (type->type_tag) { |
| case fidl::kFidlTypeStructPointer: |
| case fidl::kFidlTypeTablePointer: |
| case fidl::kFidlTypeUnionPointer: |
| return sizeof(uint64_t); |
| case fidl::kFidlTypeHandle: |
| return sizeof(zx_handle_t); |
| case fidl::kFidlTypeStruct: |
| return type->coded_struct.size; |
| case fidl::kFidlTypeTable: |
| return sizeof(fidl_vector_t); |
| case fidl::kFidlTypeUnion: |
| return type->coded_union.size; |
| case fidl::kFidlTypeString: |
| return sizeof(fidl_string_t); |
| case fidl::kFidlTypeArray: |
| return type->coded_array.array_size; |
| case fidl::kFidlTypeVector: |
| return sizeof(fidl_vector_t); |
| } |
| abort(); |
| return 0; |
| } |
| |
| // Functions that manipulate the decoding stack frames. |
| struct Frame { |
| Frame(const fidl_type_t* fidl_type, uint32_t offset) |
| : offset(offset) { |
| switch (fidl_type->type_tag) { |
| case fidl::kFidlTypeStruct: |
| state = kStateStruct; |
| struct_state.fields = fidl_type->coded_struct.fields; |
| struct_state.field_count = fidl_type->coded_struct.field_count; |
| break; |
| case fidl::kFidlTypeStructPointer: |
| state = kStateStructPointer; |
| struct_pointer_state.struct_type = fidl_type->coded_struct_pointer.struct_type; |
| break; |
| case fidl::kFidlTypeTable: |
| state = kStateTable; |
| table_state.fields = fidl_type->coded_table.fields; |
| table_state.field_count = fidl_type->coded_table.field_count; |
| table_state.present_count = 0; |
| break; |
| case fidl::kFidlTypeTablePointer: |
| state = kStateTablePointer; |
| table_pointer_state.table_type = fidl_type->coded_table_pointer.table_type; |
| break; |
| case fidl::kFidlTypeUnion: |
| state = kStateUnion; |
| union_state.types = fidl_type->coded_union.types; |
| union_state.type_count = fidl_type->coded_union.type_count; |
| union_state.data_offset = fidl_type->coded_union.data_offset; |
| break; |
| case fidl::kFidlTypeUnionPointer: |
| state = kStateUnionPointer; |
| union_pointer_state.union_type = fidl_type->coded_union_pointer.union_type; |
| break; |
| case fidl::kFidlTypeArray: |
| state = kStateArray; |
| array_state.element = fidl_type->coded_array.element; |
| array_state.array_size = fidl_type->coded_array.array_size; |
| array_state.element_size = fidl_type->coded_array.element_size; |
| break; |
| case fidl::kFidlTypeString: |
| state = kStateString; |
| string_state.max_size = fidl_type->coded_string.max_size; |
| string_state.nullable = fidl_type->coded_string.nullable; |
| break; |
| case fidl::kFidlTypeHandle: |
| state = kStateHandle; |
| handle_state.nullable = fidl_type->coded_handle.nullable; |
| break; |
| case fidl::kFidlTypeVector: |
| state = kStateVector; |
| vector_state.element = fidl_type->coded_vector.element; |
| vector_state.max_count = fidl_type->coded_vector.max_count; |
| vector_state.element_size = fidl_type->coded_vector.element_size; |
| vector_state.nullable = fidl_type->coded_vector.nullable; |
| break; |
| } |
| } |
| |
| Frame(const fidl::FidlCodedStruct* coded_struct, uint32_t offset) |
| : offset(offset) { |
| state = kStateStruct; |
| struct_state.fields = coded_struct->fields; |
| struct_state.field_count = coded_struct->field_count; |
| } |
| |
| Frame(const fidl::FidlCodedTable* coded_table, uint32_t offset) |
| : offset(offset) { |
| state = kStateStruct; |
| table_state.fields = coded_table->fields; |
| table_state.field_count = coded_table->field_count; |
| } |
| |
| Frame(const fidl::FidlCodedUnion* coded_union, uint32_t offset) |
| : offset(offset) { |
| state = kStateUnion; |
| union_state.types = coded_union->types; |
| union_state.type_count = coded_union->type_count; |
| union_state.data_offset = coded_union->data_offset; |
| } |
| |
| Frame(const fidl_type_t* element, uint32_t array_size, uint32_t element_size, |
| uint32_t offset) |
| : offset(offset) { |
| state = kStateArray; |
| array_state.element = element; |
| array_state.array_size = array_size; |
| array_state.element_size = element_size; |
| } |
| |
| // The default constructor does nothing when initializing the stack of frames. |
| Frame() {} |
| |
| static Frame DoneSentinel() { |
| Frame frame; |
| frame.state = kStateDone; |
| return frame; |
| } |
| |
| uint32_t NextStructField() { |
| ZX_DEBUG_ASSERT(state == kStateStruct); |
| |
| uint32_t current = field; |
| field += 1; |
| return current; |
| } |
| |
| uint32_t NextArrayOffset() { |
| ZX_DEBUG_ASSERT(state == kStateArray); |
| |
| uint32_t current = field; |
| field += array_state.element_size; |
| return current; |
| } |
| |
| enum : int { |
| kStateStruct, |
| kStateStructPointer, |
| kStateTable, |
| kStateTablePointer, |
| kStateUnion, |
| kStateUnionPointer, |
| kStateArray, |
| kStateString, |
| kStateHandle, |
| kStateVector, |
| |
| kStateDone, |
| } state; |
| // A byte offset into bytes_; |
| uint32_t offset; |
| |
| // This is a subset of the information recorded in the |
| // fidl_type structures needed for decoding state. For |
| // example, struct sizes do not need to be present here. |
| union { |
| struct { |
| const fidl::FidlField* fields; |
| uint32_t field_count; |
| } struct_state; |
| struct { |
| const fidl::FidlCodedStruct* struct_type; |
| } struct_pointer_state; |
| struct { |
| const fidl::FidlTableField* fields; |
| uint32_t known_index; |
| uint32_t field_count; |
| uint32_t present_count; |
| uint32_t end_offset; |
| uint32_t end_handle; |
| } table_state; |
| struct { |
| const fidl::FidlCodedTable* table_type; |
| } table_pointer_state; |
| struct { |
| const fidl_type_t* const* types; |
| uint32_t type_count; |
| uint32_t data_offset; |
| } union_state; |
| struct { |
| const fidl::FidlCodedUnion* union_type; |
| } union_pointer_state; |
| struct { |
| const fidl_type_t* element; |
| uint32_t array_size; |
| uint32_t element_size; |
| } array_state; |
| struct { |
| uint32_t max_size; |
| bool nullable; |
| } string_state; |
| struct { |
| bool nullable; |
| } handle_state; |
| struct { |
| const fidl_type* element; |
| uint32_t max_count; |
| uint32_t element_size; |
| bool nullable; |
| } vector_state; |
| }; |
| |
| uint32_t field = 0u; |
| }; |
| |
| // Returns true on success and false on recursion overflow. |
| bool Push(Frame frame) { |
| if (depth_ == FIDL_RECURSION_DEPTH) { |
| return false; |
| } |
| decoding_frames_[depth_] = frame; |
| ++depth_; |
| return true; |
| } |
| |
| void Pop() { |
| ZX_DEBUG_ASSERT(depth_ != 0u); |
| --depth_; |
| } |
| |
| Frame* Peek() { |
| ZX_DEBUG_ASSERT(depth_ != 0u); |
| return &decoding_frames_[depth_ - 1]; |
| } |
| |
| // Message state passed in to the constructor. |
| const fidl_type_t* const type_; |
| |
| // Internal state. |
| uint32_t handle_idx_ = 0u; |
| uint32_t out_of_line_offset_ = 0u; |
| |
| // Decoding stack state. |
| uint32_t depth_ = 0u; |
| Frame decoding_frames_[FIDL_RECURSION_DEPTH]; |
| }; |
| |
| } // namespace internal |
| } // namespace fidl |