blob: 93d9484f23964b8a2b28c05bdd80aac277df5d83 [file] [log] [blame]
// 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.
#ifndef ZIRCON_SYSTEM_ULIB_FIDL_BUFFER_WALKER_H_
#define ZIRCON_SYSTEM_ULIB_FIDL_BUFFER_WALKER_H_
#include <lib/fidl/internal.h>
#include <cstdlib>
#include <cstdint>
#include <cstdlib>
#include <stdalign.h>
#include <type_traits>
#include <lib/fidl/coding.h>
#include <lib/fidl/internal.h>
#include <zircon/assert.h>
#include <zircon/compiler.h>
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::FidlStructField& 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 envelope_hdr_offset = static_cast<uint32_t>(
frame->offset + (frame->field - 1) * 2 * sizeof(uint64_t));
const uint32_t data_offset = static_cast<uint32_t>(
envelope_hdr_offset + sizeof(uint64_t));
const uint64_t packed_sizes = *TypedAt<uint64_t>(envelope_hdr_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("table is larger than expected");
FIDL_POP_AND_CONTINUE_OR_RETURN;
}
if (add_overflow(handle_idx_, table_handles, &handles) ||
handles > num_handles()) {
SetError("table has more handles than expected");
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::kStateXUnion: {
auto xunion = TypedAt<fidl_xunion_t>(frame->offset);
if (xunion->padding != 0) {
SetError("xunion padding after discriminant are non-zero");
FIDL_POP_AND_CONTINUE_OR_RETURN;
}
uint32_t end_offset;
if (add_overflow(out_of_line_offset_, xunion->envelope.num_bytes, &end_offset) || end_offset > num_bytes()) {
SetError("xunion size is larger than expected");
FIDL_POP_AND_CONTINUE_OR_RETURN;
}
uint32_t total_handle_count;
if (add_overflow(handle_idx_, xunion->envelope.num_handles, &total_handle_count) ||
total_handle_count > num_handles()) {
SetError("xunion has more handles than expected");
FIDL_POP_AND_CONTINUE_OR_RETURN;
}
switch (GetPointerState(&xunion->envelope.data)) {
case PointerState::PRESENT:
if (xunion->tag == 0) {
SetError("xunion has zero discriminant but envelope is present");
FIDL_POP_AND_CONTINUE_OR_RETURN;
}
break;
case PointerState::ABSENT:
if (xunion->tag != 0) {
SetError("xunion has non-zero discriminant but no data");
FIDL_POP_AND_CONTINUE_OR_RETURN;
}
// Empty xunion.
frame->offset += static_cast<uint32_t>(sizeof(fidl_xunion_t));
Pop();
continue;
case PointerState::INVALID:
default:
SetError("xunion has invalid envelope pointer");
FIDL_POP_AND_CONTINUE_OR_RETURN;
break;
}
const FidlXUnionField* known_field = nullptr;
for (size_t i = 0; i < frame->xunion_state.field_count; i++) {
const auto field = frame->xunion_state.fields + i;
if (field->ordinal == xunion->tag) {
known_field = field;
break;
}
}
auto claim_data = [&](uint32_t known_size, uint32_t* envelope_offset) {
if (!ClaimOutOfLineStorage(known_size, xunion->envelope.data, envelope_offset)) {
SetError("xunion out-of-line storage claim couldn't be validated");
return false;
}
if (*envelope_offset + xunion->envelope.num_bytes != end_offset) {
SetError("expected xunion end offset doesn't match envelope offset + recursive size");
return false;
}
UpdatePointer(&xunion->envelope.data, TypedAt<void>(*envelope_offset));
return true;
};
uint32_t envelope_offset;
if (known_field != nullptr) {
if (!claim_data(TypeSize(known_field->type), &envelope_offset)) {
FIDL_POP_AND_CONTINUE_OR_RETURN;
}
frame->offset = envelope_offset;
*frame = Frame(known_field->type, envelope_offset);
} else {
if (!claim_data(xunion->envelope.num_bytes, &envelope_offset)) {
FIDL_POP_AND_CONTINUE_OR_RETURN;
}
for (uint32_t i = 0; i < xunion->envelope.num_handles; i++) {
if (!ClaimHandle(nullptr)) {
SetError("expected handle not present");
FIDL_POP_AND_CONTINUE_OR_RETURN;
}
}
frame->offset = end_offset;
Pop();
}
continue;
}
case Frame::kStateXUnionPointer: {
auto xunion_ptr_ptr = TypedAt<fidl_xunion_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 xunion pointer");
FIDL_POP_AND_CONTINUE_OR_RETURN;
}
if (!ClaimOutOfLineStorage(sizeof(fidl_xunion_t), *xunion_ptr_ptr,
&frame->offset)) {
SetError("message wanted to store too large of a nullable xunion");
FIDL_POP_AND_CONTINUE_OR_RETURN;
}
UpdatePointer(xunion_ptr_ptr, TypedAt<fidl_xunion_tag_t>(frame->offset));
const fidl::FidlCodedXUnion* coded_xunion = frame->xunion_pointer_state.xunion_type;
*frame = Frame(coded_xunion, 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;
case HandleState::INVALID:
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:
case fidl::kFidlTypeXUnionPointer:
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::kFidlTypeXUnion:
return sizeof(fidl_xunion_t);
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::kFidlTypeXUnion:
state = kStateXUnion;
xunion_state.fields = fidl_type->coded_xunion.fields;
xunion_state.field_count = fidl_type->coded_xunion.field_count;
break;
case fidl::kFidlTypeXUnionPointer:
state = kStateXUnionPointer;
xunion_pointer_state.xunion_type = fidl_type->coded_xunion_pointer.xunion_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::FidlCodedXUnion* coded_xunion, uint32_t offset)
: state(kStateXUnion), offset(offset) {
// This initialization is done in the ctor body instead of in an
// initialization list since we need to set fields in unions, which
// is much more involved in a ctor initialization list.
xunion_state.fields = coded_xunion->fields;
xunion_state.field_count = coded_xunion->field_count;
}
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,
kStateXUnion,
kStateXUnionPointer,
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::FidlStructField* 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::FidlXUnionField* fields;
uint32_t field_count;
} xunion_state;
struct {
const fidl::FidlCodedXUnion* xunion_type;
} xunion_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
#endif // ZIRCON_SYSTEM_ULIB_FIDL_BUFFER_WALKER_H_