Add Fuchsia SDK.
Bug: 120856007
Test: Build walleye.
Change-Id: I6b5ebe9bff4e729783bca67d35c291878fbe16cb
diff --git a/pkg/fidl/buffer_walker.h b/pkg/fidl/buffer_walker.h
new file mode 100644
index 0000000..92cb2e1
--- /dev/null
+++ b/pkg/fidl/buffer_walker.h
@@ -0,0 +1,809 @@
+// 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
diff --git a/pkg/fidl/builder.cpp b/pkg/fidl/builder.cpp
new file mode 100644
index 0000000..476b7ee
--- /dev/null
+++ b/pkg/fidl/builder.cpp
@@ -0,0 +1,62 @@
+// Copyright 2018 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.
+
+#include <lib/fidl/cpp/builder.h>
+
+#include <string.h>
+
+#include <lib/fidl/internal.h>
+
+namespace fidl {
+
+Builder::Builder()
+ : capacity_(0u), at_(0u), buffer_(nullptr) {}
+
+Builder::Builder(void* buffer, uint32_t capacity)
+ : capacity_(capacity), at_(0u), buffer_(static_cast<uint8_t*>(buffer)) {
+}
+
+Builder::~Builder() = default;
+
+Builder::Builder(Builder&& other)
+ : capacity_(other.capacity_),
+ at_(other.at_),
+ buffer_(other.buffer_) {
+ other.Reset(nullptr, 0);
+}
+
+Builder& Builder::operator=(Builder&& other) {
+ if (this != &other) {
+ capacity_ = other.capacity_;
+ at_ = other.at_;
+ buffer_ = other.buffer_;
+ other.Reset(nullptr, 0);
+ }
+ return *this;
+}
+
+void* Builder::Allocate(uint32_t size) {
+ uint64_t limit = FidlAlign(at_ + size);
+ if (limit > capacity_)
+ return nullptr;
+ uint8_t* result = &buffer_[at_];
+ memset(buffer_ + at_, 0, limit - at_);
+ at_ = static_cast<uint32_t>(limit);
+ return result;
+}
+
+BytePart Builder::Finalize() {
+ BytePart bytes(buffer_, capacity_, at_);
+ capacity_ = 0u;
+ at_ = 0u;
+ return bytes;
+}
+
+void Builder::Reset(void* buffer, uint32_t capacity) {
+ buffer_ = static_cast<uint8_t*>(buffer);
+ capacity_ = capacity;
+ at_ = 0u;
+}
+
+} // namespace fidl
diff --git a/pkg/fidl/decoding.cpp b/pkg/fidl/decoding.cpp
new file mode 100644
index 0000000..3066778
--- /dev/null
+++ b/pkg/fidl/decoding.cpp
@@ -0,0 +1,115 @@
+// 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.
+
+#include <lib/fidl/coding.h>
+
+#include <stdalign.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <lib/fidl/internal.h>
+#include <zircon/assert.h>
+#include <zircon/compiler.h>
+
+#ifdef __Fuchsia__
+#include <zircon/syscalls.h>
+#endif
+
+#include "buffer_walker.h"
+
+// TODO(kulakowski) Design zx_status_t error values.
+
+namespace {
+
+class FidlDecoder final : public fidl::internal::BufferWalker<FidlDecoder, true, false> {
+ typedef fidl::internal::BufferWalker<FidlDecoder, true, false> Super;
+
+public:
+ FidlDecoder(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ const zx_handle_t* handles, uint32_t num_handles, const char** out_error_msg)
+ : Super(type), bytes_(static_cast<uint8_t*>(bytes)), num_bytes_(num_bytes),
+ handles_(handles), num_handles_(num_handles), out_error_msg_(out_error_msg) {}
+
+ void Walk() {
+ if (handles_ == nullptr && num_handles_ != 0u) {
+ SetError("Cannot provide non-zero handle count and null handle pointer");
+ return;
+ }
+ Super::Walk();
+ if (status_ == ZX_OK && handle_idx() != num_handles()) {
+ SetError("message did not contain the specified number of handles");
+ }
+ }
+
+ uint8_t* bytes() const { return bytes_; }
+ uint32_t num_bytes() const { return num_bytes_; }
+ uint32_t num_handles() const { return num_handles_; }
+
+ bool ValidateOutOfLineStorageClaim(const void* a, const void* b) {
+ return true;
+ }
+
+ void UnclaimedHandle(zx_handle_t* out_handle) {}
+ void ClaimedHandle(zx_handle_t* out_handle, uint32_t idx) {
+ if (out_handle != nullptr) {
+ *out_handle = handles_[idx];
+#ifdef __Fuchsia__
+ } else {
+ // Return value intentionally ignored: this is best-effort cleanup.
+ zx_handle_close(handles_[idx]);
+#endif
+ }
+ }
+
+ PointerState GetPointerState(const void* ptr) const {
+ return static_cast<PointerState>(*static_cast<const uintptr_t*>(ptr));
+ }
+ HandleState GetHandleState(zx_handle_t p) const {
+ return static_cast<HandleState>(p);
+ }
+
+ template <class T>
+ void UpdatePointer(T* p, T v) {
+ *p = v;
+ }
+
+ void SetError(const char* error_msg) {
+ status_ = ZX_ERR_INVALID_ARGS;
+ if (out_error_msg_ != nullptr) {
+ *out_error_msg_ = error_msg;
+ }
+#ifdef __Fuchsia__
+ if (handles_) {
+ // Return value intentionally ignored: this is best-effort cleanup.
+ zx_handle_close_many(handles_, num_handles());
+ }
+#endif
+ }
+
+ zx_status_t status() const { return status_; }
+
+private:
+ uint8_t* const bytes_;
+ const uint32_t num_bytes_;
+ const zx_handle_t* const handles_;
+ const uint32_t num_handles_;
+ const char** const out_error_msg_;
+ zx_status_t status_ = ZX_OK;
+};
+
+} // namespace
+
+zx_status_t fidl_decode(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ const zx_handle_t* handles, uint32_t num_handles,
+ const char** out_error_msg) {
+ FidlDecoder decoder(type, bytes, num_bytes, handles, num_handles, out_error_msg);
+ decoder.Walk();
+ return decoder.status();
+}
+
+zx_status_t fidl_decode_msg(const fidl_type_t* type, fidl_msg_t* msg,
+ const char** out_error_msg) {
+ return fidl_decode(type, msg->bytes, msg->num_bytes, msg->handles,
+ msg->num_handles, out_error_msg);
+}
diff --git a/pkg/fidl/encoding.cpp b/pkg/fidl/encoding.cpp
new file mode 100644
index 0000000..f4d4f49
--- /dev/null
+++ b/pkg/fidl/encoding.cpp
@@ -0,0 +1,134 @@
+// 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.
+
+#include <lib/fidl/coding.h>
+
+#include <stdalign.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <lib/fidl/internal.h>
+#include <zircon/assert.h>
+#include <zircon/compiler.h>
+
+#ifdef __Fuchsia__
+#include <zircon/syscalls.h>
+#endif
+
+#include "buffer_walker.h"
+
+#include <stdio.h>
+
+// TODO(kulakowski) Design zx_status_t error values.
+
+namespace {
+
+class FidlEncoder final : public fidl::internal::BufferWalker<FidlEncoder, true, true> {
+ typedef fidl::internal::BufferWalker<FidlEncoder, true, true> Super;
+
+public:
+ FidlEncoder(const fidl_type_t* type, void* bytes, uint32_t num_bytes, zx_handle_t* handles,
+ uint32_t num_handles, uint32_t* out_actual_handles, const char** out_error_msg)
+ : Super(type), bytes_(static_cast<uint8_t*>(bytes)), num_bytes_(num_bytes),
+ handles_(handles), num_handles_(num_handles), out_actual_handles_(out_actual_handles),
+ out_error_msg_(out_error_msg) {}
+
+ void Walk() {
+ if (handles_ == nullptr && num_handles_ != 0u) {
+ SetError("Cannot provide non-zero handle count and null handle pointer");
+ return;
+ }
+ if (out_actual_handles_ == nullptr) {
+ SetError("Cannot encode with null out_actual_handles");
+ return;
+ }
+ Super::Walk();
+ if (status_ == ZX_OK) {
+ *out_actual_handles_ = handle_idx();
+ }
+ }
+
+ uint8_t* bytes() const { return bytes_; }
+ uint32_t num_bytes() const { return num_bytes_; }
+ uint32_t num_handles() const { return num_handles_; }
+
+ bool ValidateOutOfLineStorageClaim(const void* a, const void* b) {
+ return a == b;
+ }
+
+ void UnclaimedHandle(zx_handle_t* out_handle) {
+#ifdef __Fuchsia__
+ // Return value intentionally ignored: this is best-effort cleanup.
+ zx_handle_close(*out_handle);
+#endif
+ }
+ void ClaimedHandle(zx_handle_t* out_handle, uint32_t idx) {
+ assert(out_handle != nullptr);
+ handles_[idx] = *out_handle;
+ *out_handle = FIDL_HANDLE_PRESENT;
+ }
+
+ PointerState GetPointerState(const void* ptr) const {
+ return *static_cast<const uintptr_t*>(ptr) == 0
+ ? PointerState::ABSENT
+ : PointerState::PRESENT;
+ }
+ HandleState GetHandleState(zx_handle_t p) const {
+ return p == ZX_HANDLE_INVALID
+ ? HandleState::ABSENT
+ : HandleState::PRESENT;
+ }
+
+ template <class T>
+ void UpdatePointer(T** p, T* v) {
+ assert(*p == v);
+ assert(v != nullptr);
+ *p = reinterpret_cast<T*>(FIDL_ALLOC_PRESENT);
+ }
+
+ void SetError(const char* error_msg) {
+ if (status_ != ZX_OK) {
+ return;
+ }
+ status_ = ZX_ERR_INVALID_ARGS;
+ if (out_error_msg_ != nullptr) {
+ *out_error_msg_ = error_msg;
+ }
+#ifdef __Fuchsia__
+ if (handles_) {
+ // Return value intentionally ignored: this is best-effort cleanup.
+ zx_handle_close_many(handles_, num_handles());
+ }
+#endif
+ }
+
+ zx_status_t status() const { return status_; }
+
+private:
+ // Message state passed in to the constructor.
+ uint8_t* const bytes_;
+ const uint32_t num_bytes_;
+ zx_handle_t* const handles_;
+ const uint32_t num_handles_;
+ uint32_t* const out_actual_handles_;
+ const char** const out_error_msg_;
+ zx_status_t status_ = ZX_OK;
+};
+
+} // namespace
+
+zx_status_t fidl_encode(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ zx_handle_t* handles, uint32_t max_handles, uint32_t* out_actual_handles,
+ const char** out_error_msg) {
+ FidlEncoder encoder(type, bytes, num_bytes, handles, max_handles, out_actual_handles,
+ out_error_msg);
+ encoder.Walk();
+ return encoder.status();
+}
+
+zx_status_t fidl_encode_msg(const fidl_type_t* type, fidl_msg_t* msg,
+ uint32_t* out_actual_handles, const char** out_error_msg) {
+ return fidl_encode(type, msg->bytes, msg->num_bytes, msg->handles, msg->num_handles,
+ out_actual_handles, out_error_msg);
+}
diff --git a/pkg/fidl/epitaph.c b/pkg/fidl/epitaph.c
new file mode 100644
index 0000000..d1a14e1
--- /dev/null
+++ b/pkg/fidl/epitaph.c
@@ -0,0 +1,22 @@
+// Copyright 2018 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.
+
+#ifdef __Fuchsia__
+
+#include <string.h>
+
+#include <lib/fidl/epitaph.h>
+#include <zircon/fidl.h>
+#include <zircon/syscalls.h>
+
+zx_status_t fidl_epitaph_write(zx_handle_t channel, zx_status_t error) {
+ fidl_epitaph_t epitaph;
+ memset(&epitaph, 0, sizeof(epitaph));
+ epitaph.hdr.ordinal = FIDL_EPITAPH_ORDINAL;
+ epitaph.hdr.reserved0 = error;
+
+ return zx_channel_write(channel, 0, &epitaph, sizeof(epitaph), NULL, 0);
+}
+
+#endif // __Fuchsia__
diff --git a/pkg/fidl/formatting.cpp b/pkg/fidl/formatting.cpp
new file mode 100644
index 0000000..ffe6f39
--- /dev/null
+++ b/pkg/fidl/formatting.cpp
@@ -0,0 +1,218 @@
+// 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.
+
+#include <lib/fidl/coding.h>
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <lib/fidl/internal.h>
+#include <zircon/assert.h>
+#include <zircon/compiler.h>
+
+namespace {
+
+class StringBuilder {
+public:
+ StringBuilder(char* buffer, size_t capacity)
+ : buffer_(buffer), capacity_(capacity) {}
+
+ size_t length() const { return length_; }
+
+ void Append(const char* data, size_t length) {
+ size_t remaining = capacity_ - length_;
+ if (length > remaining) {
+ length = remaining;
+ }
+ memcpy(buffer_ + length_, data, length);
+ length_ += length;
+ }
+
+ void Append(const char* data) {
+ Append(data, strlen(data));
+ }
+
+ void AppendPrintf(const char* format, ...) __PRINTFLIKE(2, 3) {
+ va_list ap;
+ va_start(ap, format);
+ AppendVPrintf(format, ap);
+ va_end(ap);
+ }
+
+ void AppendVPrintf(const char* format, va_list ap) {
+ size_t remaining = capacity_ - length_;
+ if (remaining == 0u) {
+ return;
+ }
+ int count = vsnprintf(buffer_ + length_, remaining, format, ap);
+ if (count <= 0) {
+ return;
+ }
+ size_t length = static_cast<size_t>(count);
+ length_ += (length >= remaining ? remaining : length);
+ }
+
+private:
+ char* buffer_;
+ size_t capacity_;
+ size_t length_ = 0u;
+};
+
+void FormatNullability(StringBuilder* str, fidl::FidlNullability nullable) {
+ if (nullable == fidl::kNullable) {
+ str->Append("?");
+ }
+}
+
+void FormatStructName(StringBuilder* str, const fidl::FidlCodedStruct* coded_struct) {
+ if (coded_struct->name) {
+ str->Append(coded_struct->name);
+ } else {
+ str->Append("struct");
+ }
+}
+
+void FormatUnionName(StringBuilder* str, const fidl::FidlCodedUnion* coded_union) {
+ if (coded_union->name) {
+ str->Append(coded_union->name);
+ } else {
+ str->Append("union");
+ }
+}
+
+void FormatTypeName(StringBuilder* str, const fidl_type_t* type);
+void FormatElementName(StringBuilder* str, const fidl_type_t* type) {
+ if (type) {
+ FormatTypeName(str, type);
+ } else {
+ // TODO(jeffbrown): Print the actual primitive type name, assuming we
+ // start recording that information in the tables.
+ str->Append("primitive");
+ }
+}
+
+void FormatTypeName(StringBuilder* str, const fidl_type_t* type) {
+ switch (type->type_tag) {
+ case fidl::kFidlTypeStruct:
+ FormatStructName(str, &type->coded_struct);
+ break;
+ case fidl::kFidlTypeStructPointer:
+ FormatStructName(str, type->coded_struct_pointer.struct_type);
+ str->Append("?");
+ break;
+ case fidl::kFidlTypeUnion:
+ FormatUnionName(str, &type->coded_union);
+ break;
+ case fidl::kFidlTypeUnionPointer:
+ FormatUnionName(str, type->coded_union_pointer.union_type);
+ str->Append("?");
+ break;
+ case fidl::kFidlTypeArray:
+ str->Append("array<");
+ FormatElementName(str, type->coded_array.element);
+ str->Append(">");
+ str->AppendPrintf(":%" PRIu32, type->coded_array.array_size /
+ type->coded_array.element_size);
+ break;
+ case fidl::kFidlTypeString:
+ str->Append("string");
+ if (type->coded_string.max_size != FIDL_MAX_SIZE) {
+ str->AppendPrintf(":%" PRIu32, type->coded_string.max_size);
+ }
+ FormatNullability(str, type->coded_string.nullable);
+ break;
+ case fidl::kFidlTypeHandle:
+ str->Append("handle");
+ if (type->coded_handle.handle_subtype) {
+ str->Append("<");
+ switch (type->coded_handle.handle_subtype) {
+ case fidl::kFidlHandleSubtypeHandle:
+ str->Append("handle");
+ break;
+ case fidl::kFidlHandleSubtypeProcess:
+ str->Append("process");
+ break;
+ case fidl::kFidlHandleSubtypeThread:
+ str->Append("thread");
+ break;
+ case fidl::kFidlHandleSubtypeVmo:
+ str->Append("vmo");
+ break;
+ case fidl::kFidlHandleSubtypeChannel:
+ str->Append("channel");
+ break;
+ case fidl::kFidlHandleSubtypeEvent:
+ str->Append("event");
+ break;
+ case fidl::kFidlHandleSubtypePort:
+ str->Append("port");
+ break;
+ case fidl::kFidlHandleSubtypeInterrupt:
+ str->Append("interrupt");
+ break;
+ case fidl::kFidlHandleSubtypeLog:
+ str->Append("log");
+ break;
+ case fidl::kFidlHandleSubtypeSocket:
+ str->Append("socket");
+ break;
+ case fidl::kFidlHandleSubtypeResource:
+ str->Append("resource");
+ break;
+ case fidl::kFidlHandleSubtypeEventpair:
+ str->Append("eventpair");
+ break;
+ case fidl::kFidlHandleSubtypeJob:
+ str->Append("job");
+ break;
+ case fidl::kFidlHandleSubtypeVmar:
+ str->Append("vmar");
+ break;
+ case fidl::kFidlHandleSubtypeFifo:
+ str->Append("fifo");
+ break;
+ case fidl::kFidlHandleSubtypeGuest:
+ str->Append("guest");
+ break;
+ case fidl::kFidlHandleSubtypeTimer:
+ str->Append("timer");
+ break;
+ // TODO(pascallouis): Add support for iomap, pci, and hypervisor
+ // when they are supported in FIDL.
+ default:
+ str->AppendPrintf("%" PRIu32, type->coded_handle.handle_subtype);
+ break;
+ }
+ str->Append(">");
+ }
+ FormatNullability(str, type->coded_handle.nullable);
+ break;
+ case fidl::kFidlTypeVector:
+ str->Append("vector<");
+ FormatElementName(str, type->coded_vector.element);
+ str->Append(">");
+ if (type->coded_vector.max_count != FIDL_MAX_SIZE) {
+ str->AppendPrintf(":%" PRIu32, type->coded_vector.max_count);
+ }
+ FormatNullability(str, type->coded_vector.nullable);
+ break;
+ default:
+ ZX_PANIC("unrecognized tag");
+ break;
+ }
+}
+
+} // namespace
+
+size_t fidl_format_type_name(const fidl_type_t* type,
+ char* buffer, size_t capacity) {
+ if (!type || !buffer || !capacity) {
+ return 0u;
+ }
+
+ StringBuilder str(buffer, capacity);
+ FormatTypeName(&str, type);
+ return str.length();
+}
diff --git a/pkg/fidl/handle_closing.cpp b/pkg/fidl/handle_closing.cpp
new file mode 100644
index 0000000..5350993
--- /dev/null
+++ b/pkg/fidl/handle_closing.cpp
@@ -0,0 +1,108 @@
+// Copyright 2018 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.
+
+
+#include <limits>
+#include <stdalign.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <lib/fidl/coding.h>
+
+#ifdef __Fuchsia__
+
+#include <lib/fidl/internal.h>
+#include <zircon/assert.h>
+#include <zircon/compiler.h>
+#include <zircon/syscalls.h>
+
+#include "buffer_walker.h"
+
+namespace {
+
+class FidlHandleCloser final : public fidl::internal::BufferWalker<FidlHandleCloser, true, true> {
+ typedef fidl::internal::BufferWalker<FidlHandleCloser, true, true> Super;
+
+public:
+ FidlHandleCloser(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ const char** out_error_msg)
+ : Super(type), bytes_(static_cast<uint8_t*>(bytes)), num_bytes_(num_bytes),
+ out_error_msg_(out_error_msg) {}
+
+ void Walk() {
+ Super::Walk();
+ }
+
+ uint8_t* bytes() const { return bytes_; }
+ uint32_t num_bytes() const { return num_bytes_; }
+ uint32_t num_handles() const { return std::numeric_limits<uint32_t>::max(); }
+
+ bool ValidateOutOfLineStorageClaim(const void* a, void* b) {
+ return true;
+ }
+
+ void UnclaimedHandle(zx_handle_t* out_handle) {
+ // This will never happen since we are returning numeric_limits::max() in num_handles.
+ // We want to claim (close) all the handles.
+ ZX_DEBUG_ASSERT(false);
+ }
+
+ void ClaimedHandle(zx_handle_t* out_handle, uint32_t idx) {
+ if (*out_handle != ZX_HANDLE_INVALID) {
+ zx_handle_close(*out_handle);
+ }
+ *out_handle = ZX_HANDLE_INVALID;
+ }
+
+ template <class T>
+ void UpdatePointer(T* const* p, T* v) {}
+
+ PointerState GetPointerState(const void* ptr) const {
+ return *static_cast<const uintptr_t*>(ptr) == 0
+ ? PointerState::ABSENT
+ : PointerState::PRESENT;
+ }
+
+ HandleState GetHandleState(zx_handle_t p) const {
+ // Treat all handles as present to keep the buffer walker going.
+ return HandleState::PRESENT;
+ }
+
+ void SetError(const char* error_msg) {
+ status_ = ZX_ERR_INVALID_ARGS;
+ if (out_error_msg_ != nullptr) {
+ *out_error_msg_ = error_msg;
+ }
+ }
+
+ zx_status_t status() const { return status_; }
+
+private:
+ // Message state passed in to the constructor.
+ uint8_t* const bytes_;
+ const uint32_t num_bytes_;
+ const char** const out_error_msg_;
+ zx_status_t status_ = ZX_OK;
+};
+
+} // namespace
+
+#endif // __Fuchsia__
+
+zx_status_t fidl_close_handles(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ const char** out_error_msg) {
+#if __Fuchsia__
+ FidlHandleCloser handle_closer(type, bytes, num_bytes, out_error_msg);
+ handle_closer.Walk();
+ return handle_closer.status();
+#else
+ return ZX_OK; // there can't be any handles on the host
+#endif
+}
+
+zx_status_t fidl_close_handles_msg(const fidl_type_t* type, const fidl_msg_t* msg,
+ const char** out_error_msg) {
+ return fidl_close_handles(type, msg->bytes, msg->num_bytes, out_error_msg);
+}
+
diff --git a/pkg/fidl/include/lib/fidl/coding.h b/pkg/fidl/include/lib/fidl/coding.h
new file mode 100644
index 0000000..f30fe94
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/coding.h
@@ -0,0 +1,60 @@
+// 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 LIB_FIDL_CODING_H_
+#define LIB_FIDL_CODING_H_
+
+#include <zircon/compiler.h>
+#include <zircon/fidl.h>
+#include <zircon/types.h>
+
+__BEGIN_CDECLS
+
+// The maximum recursion depth the fidl encoder or decoder will
+// perform. Each nested aggregate type (structs, unions, arrays, or
+// vectors) counts as one step in the recursion depth.
+#define FIDL_RECURSION_DEPTH 32
+
+// See https://fuchsia.googlesource.com/docs/+/master/development/languages/fidl/languages/c.md#fidl_encode-fidl_encode_msg
+zx_status_t fidl_encode(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ zx_handle_t* handles, uint32_t max_handles,
+ uint32_t* out_actual_handles, const char** out_error_msg);
+zx_status_t fidl_encode_msg(const fidl_type_t* type, fidl_msg_t* msg,
+ uint32_t* out_actual_handles, const char** out_error_msg);
+
+// See https://fuchsia.googlesource.com/docs/+/master/development/languages/fidl/languages/c.md#fidl_decode-fidl_decode_msg
+zx_status_t fidl_decode(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ const zx_handle_t* handles, uint32_t num_handles,
+ const char** error_msg_out);
+zx_status_t fidl_decode_msg(const fidl_type_t* type, fidl_msg_t* msg,
+ const char** out_error_msg);
+
+// Validates an encoded message against the given |type|.
+//
+// The |bytes| are not modified.
+zx_status_t fidl_validate(const fidl_type_t* type, const void* bytes, uint32_t num_bytes,
+ uint32_t num_handles, const char** error_msg_out);
+zx_status_t fidl_validate_msg(const fidl_type_t* type, const fidl_msg_t* msg,
+ const char** out_error_msg);
+
+// Traverses a linearized FIDL message, closing all handles within it.
+// This function is a no-op on host side.
+//
+// Handle values in |bytes| are replaced with ZX_HANDLE_INVALID.
+zx_status_t fidl_close_handles(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ const char** out_error_msg);
+zx_status_t fidl_close_handles_msg(const fidl_type_t* type, const fidl_msg_t* msg,
+ const char** out_error_msg);
+
+// Stores the name of a fidl type into the provided buffer.
+// Truncates the name if it is too long to fit into the buffer.
+// Returns the number of characters written into the buffer.
+//
+// Note: This function does not write a trailing NUL.
+size_t fidl_format_type_name(const fidl_type_t* type,
+ char* buffer, size_t capacity);
+
+__END_CDECLS
+
+#endif // LIB_FIDL_CODING_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/builder.h b/pkg/fidl/include/lib/fidl/cpp/builder.h
new file mode 100644
index 0000000..bc394c5
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/builder.h
@@ -0,0 +1,104 @@
+// Copyright 2018 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 LIB_FIDL_CPP_BUILDER_H_
+#define LIB_FIDL_CPP_BUILDER_H_
+
+#include <new> // For placement new.
+#include <stdalign.h>
+#include <stdint.h>
+
+#include <lib/fidl/cpp/message_part.h>
+#include <zircon/compiler.h>
+#include <zircon/fidl.h>
+#include <zircon/types.h>
+
+namespace fidl {
+
+// Builder helps FIDL clients store decoded objects in a buffer.
+//
+// Objects are allocated sequentually in the buffer with appropriate alignment
+// for in-place encoding. The client is responsible for ordering the objects in
+// the buffer appropriately.
+class Builder {
+public:
+ // Creates a buffer without any storage.
+ Builder();
+
+ // Creates a buffer that stores objects in the given memory.
+ //
+ // The constructed |Builder| object does not take ownership of the given
+ // storage.
+ Builder(void* buffer, uint32_t capacity);
+ ~Builder();
+
+ Builder(const Builder& other) = delete;
+ Builder& operator=(const Builder& other) = delete;
+
+ Builder(Builder&& other);
+ Builder& operator=(Builder&& other);
+
+ // Allocates storage in the buffer of sufficient size to store an object of
+ // type |T|. The object must have alignment constraints that are compatible
+ // with FIDL messages.
+ //
+ // If there is insufficient storage in the builder's buffer, this method
+ // returns nullptr.
+ template <typename T>
+ T* New() {
+ static_assert(alignof(T) <= FIDL_ALIGNMENT, "");
+ static_assert(sizeof(T) <= ZX_CHANNEL_MAX_MSG_BYTES, "");
+ if (void* ptr = Allocate(sizeof(T)))
+ return new (ptr) T;
+ return nullptr;
+ }
+
+ // Allocates storage in the buffer of sufficient size to store |count|
+ // objects of type |T|. The object must have alignment constraints that are
+ // compatible with FIDL messages.
+ //
+ // If there is insufficient storage in the builder's buffer, this method
+ // returns nullptr.
+ template <typename T>
+ T* NewArray(uint32_t count) {
+ static_assert(alignof(T) <= FIDL_ALIGNMENT, "");
+ static_assert(sizeof(T) <= ZX_CHANNEL_MAX_MSG_BYTES, "");
+ if (sizeof(T) * static_cast<uint64_t>(count) > UINT32_MAX)
+ return nullptr;
+ if (void* ptr = Allocate(static_cast<uint32_t>(sizeof(T) * count)))
+ return new (ptr) T[count];
+ return nullptr;
+ }
+
+ // Completes the building and returns a |MesssagePart| containing the
+ // allocated objects.
+ //
+ // The allocated objects are placed in the returned buffer in the order in
+ // which they were allocated, with appropriate alignment for a FIDL message.
+ // The returned buffer's capacity cooresponds to the capacity originally
+ // provided to this builder in its constructor.
+ BytePart Finalize();
+
+ // Attaches the given storage to the |Builder|.
+ //
+ // The |Builder| object does not take ownership of the given storage. The
+ // next object will be allocated at the start of the buffer.
+ void Reset(void* buffer, uint32_t capacity);
+
+protected:
+ uint8_t* buffer() const { return buffer_; }
+ uint32_t capacity() const { return capacity_; }
+
+private:
+ // Returns |size| bytes of zeroed memory aligned to at least FIDL_ALIGNMENT
+ void* Allocate(uint32_t size);
+
+ uint32_t capacity_;
+ uint32_t at_;
+ uint8_t* buffer_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_BUILDER_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/message.h b/pkg/fidl/include/lib/fidl/cpp/message.h
new file mode 100644
index 0000000..d93e7c9
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/message.h
@@ -0,0 +1,172 @@
+// Copyright 2018 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 LIB_FIDL_CPP_MESSAGE_H_
+#define LIB_FIDL_CPP_MESSAGE_H_
+
+#include <lib/fidl/coding.h>
+#include <lib/fidl/cpp/message_part.h>
+#include <zircon/fidl.h>
+
+namespace fidl {
+
+// A FIDL message.
+//
+// A FIDL message has two parts: the bytes and the handles. The bytes are
+// divided into a header (of type fidl_message_header_t) and a payload, which
+// follows the header.
+//
+// A Message object does not own the storage for the message parts.
+class Message {
+public:
+ // Creates a message without any storage.
+ Message();
+
+ // Creates a message whose storage is backed by |bytes| and |handles|.
+ //
+ // The constructed |Message| object does not take ownership of the given
+ // storage, although does take ownership of zircon handles contained withing
+ // handles.
+ Message(BytePart bytes, HandlePart handles);
+
+ ~Message();
+
+ Message(const Message& other) = delete;
+ Message& operator=(const Message& other) = delete;
+
+ Message(Message&& other);
+ Message& operator=(Message&& other);
+
+ // Whether the message has enough bytes to contain a fidl_message_header_t.
+ bool has_header() const {
+ return bytes_.actual() >= sizeof(fidl_message_header_t);
+ }
+
+ // The header at the start of the message.
+ //
+ // Valid only if has_header().
+ const fidl_message_header_t& header() const {
+ return *reinterpret_cast<fidl_message_header_t*>(bytes_.data());
+ }
+ fidl_message_header_t& header() {
+ return *reinterpret_cast<fidl_message_header_t*>(bytes_.data());
+ }
+
+ // The transaction ID in the message header.
+ //
+ // Valid only if has_header().
+ zx_txid_t txid() const { return header().txid; }
+ void set_txid(zx_txid_t txid) { header().txid = txid; }
+
+ // The flags in the message header.
+ //
+ // Valid only if has_header().
+ uint32_t flags() const { return header().flags; }
+
+ // The flags in the message header.
+ //
+ // Valid only if has_header().
+ uint32_t ordinal() const { return header().ordinal; }
+
+ // The message payload that follows the header.
+ //
+ // Valid only if has_header().
+ BytePart payload() const {
+ constexpr uint32_t n = sizeof(fidl_message_header_t);
+ return BytePart(bytes_.data() + n, bytes_.capacity() - n, bytes_.actual() - n);
+ }
+
+ // The message bytes interpreted as the given type.
+ template <typename T>
+ T* GetBytesAs() const {
+ return reinterpret_cast<T*>(bytes_.data());
+ }
+
+ // The message payload that follows the header interpreted as the given type.
+ //
+ // Valid only if has_header().
+ template <typename T>
+ T* GetPayloadAs() const {
+ return reinterpret_cast<T*>(bytes_.data() + sizeof(fidl_message_header_t));
+ }
+
+ // The storage for the bytes of the message.
+ BytePart& bytes() { return bytes_; }
+ const BytePart& bytes() const { return bytes_; }
+ void set_bytes(BytePart bytes) { bytes_ = static_cast<BytePart&&>(bytes); }
+
+ // The storage for the handles of the message.
+ //
+ // When the message is encoded, the handle values are stored in this part of
+ // the message. When the message is decoded, this part of the message is
+ // empty and the handle values are stored in the bytes().
+ HandlePart& handles() { return handles_; }
+ const HandlePart& handles() const { return handles_; }
+
+ // Encodes the message in-place.
+ //
+ // The message must previously have been in a decoded state, for example,
+ // either by being built in a decoded state using a |Builder| or having been
+ // decoded using the |Decode| method.
+ zx_status_t Encode(const fidl_type_t* type, const char** error_msg_out);
+
+ // Decodes the message in-place.
+ //
+ // The message must previously have been in an encoded state, for example,
+ // either by being read from a zx_channel_t or having been encoded using the
+ // |Encode| method.
+ zx_status_t Decode(const fidl_type_t* type, const char** error_msg_out);
+
+ // Validates the message in-place.
+ //
+ // The message must already be in an encoded state, for example, either by
+ // being read from a zx_channel_t or having been created in that state.
+ //
+ // Does not modify the message.
+ zx_status_t Validate(const fidl_type_t* type, const char** error_msg_out) const;
+
+ // Read a message from the given channel.
+ //
+ // The bytes read from the channel are stored in bytes() and the handles
+ // read from the channel are stored in handles(). Existing data in these
+ // buffers is overwritten.
+ zx_status_t Read(zx_handle_t channel, uint32_t flags);
+
+ // Writes a message to the given channel.
+ //
+ // The bytes stored in bytes() are written to the channel and the handles
+ // stored in handles() are written to the channel.
+ //
+ // If this method returns ZX_OK, handles() will be empty because they were
+ // consumed by this operation.
+ zx_status_t Write(zx_handle_t channel, uint32_t flags);
+
+ // Issues a synchronous send and receive transaction on the given channel.
+ //
+ // The bytes stored in bytes() are written to the channel and the handles
+ // stored in handles() are written to the channel. The bytes read from the
+ // channel are stored in response->bytes() and the handles read from the
+ // channel are stored in response->handles().
+ //
+ // If this method returns ZX_OK, handles() will be empty because they were
+ // consumed by this operation.
+ zx_status_t Call(zx_handle_t channel, uint32_t flags, zx_time_t deadline,
+ Message* response);
+
+ // Stop tracking the handles in stored in handles(), without closing them.
+ //
+ // Typically, these handles will be extracted during decode or the
+ // message's destructor, so this function will be unnecessary. However,
+ // for clients of ulib/fidl which decode message manually, this function
+ // is necessary to prevent extracted handles from being closed.
+ void ClearHandlesUnsafe();
+
+private:
+ BytePart bytes_;
+ HandlePart handles_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_MESSAGE_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/message_buffer.h b/pkg/fidl/include/lib/fidl/cpp/message_buffer.h
new file mode 100644
index 0000000..8e85823
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/message_buffer.h
@@ -0,0 +1,58 @@
+// Copyright 2018 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 LIB_FIDL_CPP_MESSAGE_BUFFER_H_
+#define LIB_FIDL_CPP_MESSAGE_BUFFER_H_
+
+#include <stdint.h>
+
+#include <lib/fidl/cpp/builder.h>
+#include <lib/fidl/cpp/message.h>
+#include <zircon/fidl.h>
+#include <zircon/types.h>
+
+namespace fidl {
+
+class MessageBuffer {
+public:
+ // Creates a |MessageBuffer| that allocates buffers for message of the
+ // given capacities.
+ //
+ // The buffers are freed when the |MessageBuffer| is destructed.
+ explicit MessageBuffer(
+ uint32_t bytes_capacity = ZX_CHANNEL_MAX_MSG_BYTES,
+ uint32_t handles_capacity = ZX_CHANNEL_MAX_MSG_HANDLES);
+
+ // The memory that backs the message is freed by this destructor.
+ ~MessageBuffer();
+
+ // The memory in which bytes can be stored in this buffer.
+ uint8_t* bytes() const { return buffer_; }
+
+ // The total number of bytes that can be stored in this buffer.
+ uint32_t bytes_capacity() const { return bytes_capacity_; }
+
+ // The memory in which handles can be stored in this buffer.
+ zx_handle_t* handles() const;
+
+ // The total number of handles that can be stored in this buffer.
+ uint32_t handles_capacity() const { return handles_capacity_; }
+
+ // Creates a |Message| that is backed by the memory in this buffer.
+ //
+ // The returned |Message| contains no bytes or handles.
+ Message CreateEmptyMessage();
+
+ // Creates a |Builder| that is backed by the memory in this buffer.
+ Builder CreateBuilder();
+
+private:
+ uint8_t* const buffer_;
+ const uint32_t bytes_capacity_;
+ const uint32_t handles_capacity_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_MESSAGE_BUFFER_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/message_builder.h b/pkg/fidl/include/lib/fidl/cpp/message_builder.h
new file mode 100644
index 0000000..b571f3b
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/message_builder.h
@@ -0,0 +1,76 @@
+// Copyright 2018 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 LIB_FIDL_CPP_MESSAGE_BUILDER_H_
+#define LIB_FIDL_CPP_MESSAGE_BUILDER_H_
+
+#include <stdint.h>
+
+#include <lib/fidl/cpp/builder.h>
+#include <lib/fidl/cpp/message_buffer.h>
+#include <lib/fidl/cpp/message.h>
+#include <zircon/fidl.h>
+#include <zircon/types.h>
+
+namespace fidl {
+
+// A builder for FIDL messages that owns the memory for the message.
+//
+// A |MessageBuilder| is a |Builder| that uses the heap to back the memory for
+// the message. If you wish to manage the memory yourself, you can use |Builder|
+// and |Message| directly.
+//
+// Upon creation, the |MessageBuilder| creates a FIDL message header, which you
+// can modify using |header()|.
+class MessageBuilder : public Builder {
+public:
+ // Creates a |MessageBuilder| for the given |type| that allocates buffers
+ // for message of the given capacities.
+ //
+ // The bytes buffer is initialied by adding a |fidl_message_header_t|
+ // header.
+ //
+ // The buffers are freed when the |MessageBuilder| is destructed.
+ explicit MessageBuilder(
+ const fidl_type_t* type,
+ uint32_t bytes_capacity = ZX_CHANNEL_MAX_MSG_BYTES,
+ uint32_t handles_capacity = ZX_CHANNEL_MAX_MSG_HANDLES);
+
+ // The memory that backs the message is freed by this destructor.
+ ~MessageBuilder();
+
+ // The type of the message payload this object is building.
+ const fidl_type_t* type() const { return type_; }
+
+ // The header for the message.
+ //
+ // The message header is allocated by the |MessageBuilder| itself.
+ fidl_message_header_t* header() const {
+ return reinterpret_cast<fidl_message_header_t*>(buffer());
+ }
+
+ // Encodes a message of the given |type|.
+ //
+ // The memory that backs the message returned by this function is owned by
+ // the |MessageBuilder|, which means the |MessageBuilder| must remain alive
+ // as long as the |Message| object is in use.
+ //
+ // The |message| parameter might be modified even if this method returns an
+ // error.
+ zx_status_t Encode(Message* message_out, const char** error_msg_out);
+
+ // Resets all the data in the |MessageBuffer|.
+ //
+ // The underlying buffer is retained and reused. The next object will be
+ // allocated at the start of the buffer.
+ void Reset();
+
+private:
+ const fidl_type_t* type_;
+ MessageBuffer buffer_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_MESSAGE_BUILDER_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/message_part.h b/pkg/fidl/include/lib/fidl/cpp/message_part.h
new file mode 100644
index 0000000..1dd7f7b
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/message_part.h
@@ -0,0 +1,102 @@
+// Copyright 2018 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 LIB_FIDL_CPP_MESSAGE_PART_H_
+#define LIB_FIDL_CPP_MESSAGE_PART_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include <zircon/types.h>
+
+namespace fidl {
+
+// Part of a FIDL message.
+//
+// A FIDL message has two parts: the bytes and the handles. This class is used
+// to represent both kinds of parts.
+//
+// Each part of the message has a data buffer, which contains the actual data
+// for that part of the message, a capacity for that buffer, and the actual
+// amount of data stored in the buffer, which might be less that the capacity if
+// the buffer is not completely full.
+template<typename T>
+class MessagePart {
+public:
+ using value_type = T;
+ using const_iterator = const T*;
+
+ // A message part with no storage.
+ MessagePart() : data_(nullptr), capacity_(0u), actual_(0u) {}
+
+ // A message part that uses the given storage.
+ //
+ // The constructed |MessagePart| object does not take ownership of the given
+ // storage.
+ MessagePart(T* data, uint32_t capacity, uint32_t actual = 0u)
+ : data_(data), capacity_(capacity), actual_(actual) {}
+
+ MessagePart(const MessagePart& other) = delete;
+ MessagePart& operator=(const MessagePart& other) = delete;
+
+ MessagePart(MessagePart&& other)
+ : data_(other.data_),
+ capacity_(other.capacity_),
+ actual_(other.actual_) {
+ other.data_ = nullptr;
+ other.capacity_ = 0u;
+ other.actual_ = 0u;
+ }
+
+ MessagePart& operator=(MessagePart&& other) {
+ if (this == &other)
+ return *this;
+ data_ = other.data_;
+ capacity_ = other.capacity_;
+ actual_ = other.actual_;
+ other.data_ = nullptr;
+ other.capacity_ = 0u;
+ other.actual_ = 0u;
+ return *this;
+ }
+
+ // The data stored in this part of the message.
+ T* data() const { return data_; }
+
+ // The total amount of storage available for this part of the message.
+ //
+ // This part of the message might not actually use all of this storage. To
+ // determine how much storage is actually being used, see |actual()|.
+ uint32_t capacity() const { return capacity_; }
+
+ // The amount of storage that is actually being used for this part of the
+ // message.
+ //
+ // There might be more storage available than is actually being used. To
+ // determine how much storage is available, see |capacity()|.
+ uint32_t actual() const { return actual_; }
+ void set_actual(uint32_t actual) { actual_ = actual; }
+
+ T* begin() { return data_; }
+ const T* begin() const { return data_; }
+ const T* cbegin() const { return data_; }
+
+ T* end() { return data_ + actual_; }
+ const T* end() const { return data_ + actual_; }
+ const T* cend() const { return data_ + actual_; }
+
+ size_t size() const { return actual_; }
+
+private:
+ T* data_;
+ uint32_t capacity_;
+ uint32_t actual_;
+};
+
+using BytePart = MessagePart<uint8_t>;
+using HandlePart = MessagePart<zx_handle_t>;
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_MESSAGE_PART_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/string_view.h b/pkg/fidl/include/lib/fidl/cpp/string_view.h
new file mode 100644
index 0000000..88eec42
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/string_view.h
@@ -0,0 +1,46 @@
+// 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 LIB_FIDL_CPP_STRING_VIEW_H_
+#define LIB_FIDL_CPP_STRING_VIEW_H_
+
+#include <zircon/fidl.h>
+
+namespace fidl {
+
+class StringView : public fidl_string_t {
+public:
+ StringView() : fidl_string_t{} {}
+
+ uint64_t size() const { return fidl_string_t::size; }
+ void set_size(uint64_t size) { fidl_string_t::size = size; }
+
+ const char* data() const { return fidl_string_t::data; }
+ void set_data(char* data) { fidl_string_t::data = data; }
+
+ char* mutable_data() const { return fidl_string_t::data; }
+
+ bool is_null() const { return fidl_string_t::data == nullptr; }
+ bool empty() const { return fidl_string_t::size == 0; }
+
+ const char& at(size_t offset) const { return data()[offset]; }
+ char& at(size_t offset) { return mutable_data()[offset]; }
+
+ const char& operator[](size_t offset) const { return at(offset); }
+ char& operator[](size_t offset) { return at(offset); }
+
+ char* begin() { return mutable_data(); }
+ const char* begin() const { return data(); }
+ const char* cbegin() const { return data(); }
+
+ char* end() { return mutable_data() + size(); }
+ const char* end() const { return data() + size(); }
+ const char* cend() const { return data() + size(); }
+
+ fidl_string_t* impl() { return this; }
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_STRING_VIEW_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/vector_view.h b/pkg/fidl/include/lib/fidl/cpp/vector_view.h
new file mode 100644
index 0000000..df4cedf
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/vector_view.h
@@ -0,0 +1,47 @@
+// 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 LIB_FIDL_CPP_VECTOR_VIEW_H_
+#define LIB_FIDL_CPP_VECTOR_VIEW_H_
+
+#include <zircon/fidl.h>
+
+namespace fidl {
+
+template<typename T>
+class VectorView : public fidl_vector_t {
+public:
+ VectorView() : fidl_vector_t{} {}
+
+ uint64_t count() const { return fidl_vector_t::count; }
+ void set_count(uint64_t count) { fidl_vector_t::count = count; }
+
+ const T* data() const { return static_cast<T*>(fidl_vector_t::data); }
+ void set_data(T* data) { fidl_vector_t::data = data; }
+
+ T* mutable_data() const { return static_cast<T*>(fidl_vector_t::data); }
+
+ bool is_null() const { return fidl_vector_t::data == nullptr; }
+ bool empty() const { return fidl_vector_t::count == 0; }
+
+ const T& at(size_t offset) const { return data()[offset]; }
+ T& at(size_t offset) { return mutable_data()[offset]; }
+
+ const T& operator[](size_t offset) const { return at(offset); }
+ T& operator[](size_t offset) { return at(offset); }
+
+ T* begin() { return mutable_data(); }
+ const T* begin() const { return data(); }
+ const T* cbegin() const { return data(); }
+
+ T* end() { return mutable_data() + count(); }
+ const T* end() const { return data() + count(); }
+ const T* cend() const { return data() + count(); }
+
+ fidl_vector_t* impl() { return this; }
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_VECTOR_VIEW_H_
diff --git a/pkg/fidl/include/lib/fidl/epitaph.h b/pkg/fidl/include/lib/fidl/epitaph.h
new file mode 100644
index 0000000..55653a7
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/epitaph.h
@@ -0,0 +1,22 @@
+// Copyright 2018 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 LIB_FIDL_EPITAPH_H_
+#define LIB_FIDL_EPITAPH_H_
+
+#include <zircon/types.h>
+
+__BEGIN_CDECLS
+
+#ifdef __Fuchsia__
+
+// Sends an epitaph with the given values down the channel.
+// See https://fuchsia.googlesource.com/docs/+/master/development/languages/fidl/languages/c.md#fidl_epitaph_write
+zx_status_t fidl_epitaph_write(zx_handle_t channel, zx_status_t error);
+
+#endif // __Fuchsia__
+
+__END_CDECLS
+
+#endif // LIB_FIDL_EPITAPH_H_
diff --git a/pkg/fidl/include/lib/fidl/internal.h b/pkg/fidl/include/lib/fidl/internal.h
new file mode 100644
index 0000000..efef16e
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/internal.h
@@ -0,0 +1,242 @@
+// 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 LIB_FIDL_INTERNAL_H_
+#define LIB_FIDL_INTERNAL_H_
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <lib/fidl/coding.h>
+#include <zircon/syscalls/object.h>
+#include <zircon/types.h>
+
+// All sizes here are given as uint32_t. Fidl message sizes are bounded to well below UINT32_MAX.
+// This also applies to arrays and vectors. For arrays, element_count * element_size will always fit
+// with 32 bits. For vectors, max_count * element_size will always fit within 32 bits.
+
+// Pointers to other type tables within a type are always nonnull, with the exception of vectors.
+// In that case, a null pointer indicates that the element type of the vector has no interesting
+// information to be decoded (i.e. no pointers or handles). The vector type still needs to be
+// emitted as it contains the information about the size of its secondary object. Contrast this with
+// arrays: being inline, ones with no interesting coding information can be elided, just like a
+// uint32 field in a struct is elided.
+
+namespace fidl {
+
+enum FidlNullability : uint32_t {
+ kNonnullable = 0u,
+ kNullable = 1u,
+};
+
+inline uint64_t FidlAlign(uint32_t offset) {
+ constexpr uint64_t alignment_mask = FIDL_ALIGNMENT - 1;
+ return (offset + alignment_mask) & ~alignment_mask;
+}
+
+struct FidlField {
+ const fidl_type* type;
+ uint32_t offset;
+
+ constexpr FidlField(const fidl_type* type, uint32_t offset)
+ : type(type), offset(offset) {}
+};
+
+struct FidlTableField {
+ const fidl_type* type;
+ uint32_t ordinal;
+
+ constexpr FidlTableField(const fidl_type* type, uint32_t ordinal)
+ : type(type), ordinal(ordinal) {}
+};
+
+enum FidlTypeTag : uint32_t {
+ kFidlTypeStruct = 0u,
+ kFidlTypeStructPointer = 1u,
+ kFidlTypeTable = 8u,
+ kFidlTypeTablePointer = 9u,
+ kFidlTypeUnion = 2u,
+ kFidlTypeUnionPointer = 3u,
+ kFidlTypeArray = 4u,
+ kFidlTypeString = 5u,
+ kFidlTypeHandle = 6u,
+ kFidlTypeVector = 7u,
+};
+
+// Though the |size| is implied by the fields, computing that information is not the purview of this
+// library. It's easier for the compiler to stash it.
+struct FidlCodedStruct {
+ const FidlField* const fields;
+ const uint32_t field_count;
+ const uint32_t size;
+ const char* name; // may be nullptr if omitted at compile time
+
+ constexpr FidlCodedStruct(const FidlField* fields, uint32_t field_count, uint32_t size,
+ const char* name)
+ : fields(fields), field_count(field_count), size(size), name(name) {}
+};
+
+struct FidlCodedStructPointer {
+ const FidlCodedStruct* const struct_type;
+
+ constexpr explicit FidlCodedStructPointer(const FidlCodedStruct* struct_type)
+ : struct_type(struct_type) {}
+};
+
+struct FidlCodedTable {
+ const FidlTableField* const fields;
+ const uint32_t field_count;
+ const char* name; // may be nullptr if omitted at compile time
+
+ constexpr FidlCodedTable(const FidlTableField* fields, uint32_t field_count,
+ const char* name)
+ : fields(fields), field_count(field_count), name(name) {}
+};
+
+struct FidlCodedTablePointer {
+ const FidlCodedTable* const table_type;
+
+ constexpr explicit FidlCodedTablePointer(const FidlCodedTable* table_type)
+ : table_type(table_type) {}
+};
+
+// Unlike structs, union members do not have different offsets, so this points to an array of
+// |fidl_type*| rather than |FidlField|.
+//
+// On-the-wire unions begin with a tag which is an index into |types|.
+// |data_offset| is the offset of the data in the wire format (tag + padding).
+struct FidlCodedUnion {
+ const fidl_type* const* types;
+ const uint32_t type_count;
+ const uint32_t data_offset;
+ const uint32_t size;
+ const char* name; // may be nullptr if omitted at compile time
+
+ constexpr FidlCodedUnion(const fidl_type* const* types, uint32_t type_count,
+ uint32_t data_offset, uint32_t size, const char* name)
+ : types(types), type_count(type_count), data_offset(data_offset), size(size), name(name) {}
+};
+
+struct FidlCodedUnionPointer {
+ const FidlCodedUnion* const union_type;
+
+ constexpr explicit FidlCodedUnionPointer(const FidlCodedUnion* union_type)
+ : union_type(union_type) {}
+};
+
+// An array is essentially a struct with |array_size / element_size| of the same field, named at
+// |element|.
+struct FidlCodedArray {
+ const fidl_type* const element;
+ const uint32_t array_size;
+ const uint32_t element_size;
+
+ constexpr FidlCodedArray(const fidl_type* element, uint32_t array_size, uint32_t element_size)
+ : element(element), array_size(array_size), element_size(element_size) {}
+};
+
+// Note: must keep in sync with fidlc types.h HandleSubtype.
+enum FidlHandleSubtype : zx_obj_type_t {
+ // special case to indicate subtype is not specified.
+ kFidlHandleSubtypeHandle = ZX_OBJ_TYPE_NONE,
+
+ kFidlHandleSubtypeProcess = ZX_OBJ_TYPE_PROCESS,
+ kFidlHandleSubtypeThread = ZX_OBJ_TYPE_THREAD,
+ kFidlHandleSubtypeVmo = ZX_OBJ_TYPE_VMO,
+ kFidlHandleSubtypeChannel = ZX_OBJ_TYPE_CHANNEL,
+ kFidlHandleSubtypeEvent = ZX_OBJ_TYPE_EVENT,
+ kFidlHandleSubtypePort = ZX_OBJ_TYPE_PORT,
+ kFidlHandleSubtypeInterrupt = ZX_OBJ_TYPE_INTERRUPT,
+ kFidlHandleSubtypeLog = ZX_OBJ_TYPE_LOG,
+ kFidlHandleSubtypeSocket = ZX_OBJ_TYPE_SOCKET,
+ kFidlHandleSubtypeResource = ZX_OBJ_TYPE_RESOURCE,
+ kFidlHandleSubtypeEventpair = ZX_OBJ_TYPE_EVENTPAIR,
+ kFidlHandleSubtypeJob = ZX_OBJ_TYPE_JOB,
+ kFidlHandleSubtypeVmar = ZX_OBJ_TYPE_VMAR,
+ kFidlHandleSubtypeFifo = ZX_OBJ_TYPE_FIFO,
+ kFidlHandleSubtypeGuest = ZX_OBJ_TYPE_GUEST,
+ kFidlHandleSubtypeTimer = ZX_OBJ_TYPE_TIMER,
+};
+
+struct FidlCodedHandle {
+ const zx_obj_type_t handle_subtype;
+ const FidlNullability nullable;
+
+ constexpr FidlCodedHandle(uint32_t handle_subtype, FidlNullability nullable)
+ : handle_subtype(handle_subtype), nullable(nullable) {}
+
+ static_assert(ZX_OBJ_TYPE_LAST <= UINT32_MAX, "");
+};
+
+struct FidlCodedString {
+ const uint32_t max_size;
+ const FidlNullability nullable;
+
+ constexpr FidlCodedString(uint32_t max_size, FidlNullability nullable)
+ : max_size(max_size), nullable(nullable) {}
+};
+
+// Note that |max_count * element_size| is guaranteed to fit into a uint32_t. Unlike other types,
+// the |element| pointer may be null. This occurs when the element type contains no interesting bits
+// (i.e. pointers or handles).
+struct FidlCodedVector {
+ const fidl_type* const element;
+ const uint32_t max_count;
+ const uint32_t element_size;
+ const FidlNullability nullable;
+
+ constexpr FidlCodedVector(const fidl_type* element, uint32_t max_count, uint32_t element_size,
+ FidlNullability nullable)
+ : element(element), max_count(max_count), element_size(element_size), nullable(nullable) {}
+};
+
+} // namespace fidl
+
+struct fidl_type {
+ const fidl::FidlTypeTag type_tag;
+ const union {
+ const fidl::FidlCodedStruct coded_struct;
+ const fidl::FidlCodedStructPointer coded_struct_pointer;
+ const fidl::FidlCodedTable coded_table;
+ const fidl::FidlCodedTablePointer coded_table_pointer;
+ const fidl::FidlCodedUnion coded_union;
+ const fidl::FidlCodedUnionPointer coded_union_pointer;
+ const fidl::FidlCodedHandle coded_handle;
+ const fidl::FidlCodedString coded_string;
+ const fidl::FidlCodedArray coded_array;
+ const fidl::FidlCodedVector coded_vector;
+ };
+
+ constexpr fidl_type(fidl::FidlCodedStruct coded_struct)
+ : type_tag(fidl::kFidlTypeStruct), coded_struct(coded_struct) {}
+
+ constexpr fidl_type(fidl::FidlCodedStructPointer coded_struct_pointer)
+ : type_tag(fidl::kFidlTypeStructPointer), coded_struct_pointer(coded_struct_pointer) {}
+
+ constexpr fidl_type(fidl::FidlCodedTable coded_table)
+ : type_tag(fidl::kFidlTypeTable), coded_table(coded_table) {}
+
+ constexpr fidl_type(fidl::FidlCodedTablePointer coded_table_pointer)
+ : type_tag(fidl::kFidlTypeTablePointer), coded_table_pointer(coded_table_pointer) {}
+
+ constexpr fidl_type(fidl::FidlCodedUnion coded_union)
+ : type_tag(fidl::kFidlTypeUnion), coded_union(coded_union) {}
+
+ constexpr fidl_type(fidl::FidlCodedUnionPointer coded_union_pointer)
+ : type_tag(fidl::kFidlTypeUnionPointer), coded_union_pointer(coded_union_pointer) {}
+
+ constexpr fidl_type(fidl::FidlCodedHandle coded_handle)
+ : type_tag(fidl::kFidlTypeHandle), coded_handle(coded_handle) {}
+
+ constexpr fidl_type(fidl::FidlCodedString coded_string)
+ : type_tag(fidl::kFidlTypeString), coded_string(coded_string) {}
+
+ constexpr fidl_type(fidl::FidlCodedArray coded_array)
+ : type_tag(fidl::kFidlTypeArray), coded_array(coded_array) {}
+
+ constexpr fidl_type(fidl::FidlCodedVector coded_vector)
+ : type_tag(fidl::kFidlTypeVector), coded_vector(coded_vector) {}
+};
+
+#endif // LIB_FIDL_INTERNAL_H_
diff --git a/pkg/fidl/include/lib/fidl/llcpp/decoded_message.h b/pkg/fidl/include/lib/fidl/llcpp/decoded_message.h
new file mode 100644
index 0000000..52d0ec5
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/llcpp/decoded_message.h
@@ -0,0 +1,119 @@
+// Copyright 2018 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 LIB_FIDL_LLCPP_DECODED_MESSAGE_H_
+#define LIB_FIDL_LLCPP_DECODED_MESSAGE_H_
+
+#include <lib/fidl/coding.h>
+#include <lib/fidl/llcpp/encoded_message.h>
+#include <lib/fidl/llcpp/traits.h>
+#include <type_traits>
+#include <zircon/fidl.h>
+
+namespace fidl {
+
+// `DecodedMessage` manages a linearized FIDL message in decoded form.
+// It takes care of releasing all handles which were not consumed
+// (std::moved from the decoded FIDL struct) when it goes out of scope.
+template <typename FidlType>
+class DecodedMessage final {
+ static_assert(IsFidlType<FidlType>::value, "Only FIDL types allowed here");
+ static_assert(FidlType::MaxSize > 0, "Positive message size");
+
+public:
+ // Instantiates an empty message.
+ // To populate this message, decode from an EncodedMessage object.
+ DecodedMessage() = default;
+
+ // Instantiates a DecodedMessage which points to a buffer region with caller-managed memory.
+ // The buffer region is assumed to contain a linearized FIDL message with valid pointers.
+ // This does not take ownership of that buffer region.
+ // But it does take ownership of the handles within the buffer.
+ DecodedMessage(BytePart bytes) :
+ bytes_(std::move(bytes)) { }
+
+ DecodedMessage(DecodedMessage&& other) = default;
+
+ DecodedMessage& operator=(DecodedMessage&& other) = default;
+
+ DecodedMessage(const DecodedMessage& other) = delete;
+
+ DecodedMessage& operator=(const DecodedMessage& other) = delete;
+
+ ~DecodedMessage() {
+ CloseHandles();
+ }
+
+ // Keeps track of a new buffer region with caller-managed memory.
+ // The buffer region is assumed to contain a linearized FIDL message with valid pointers.
+ // This does not take ownership of that buffer region.
+ // But it does take ownership of the handles within the buffer.
+ void Reset(BytePart bytes) {
+ CloseHandles();
+ bytes_ = std::move(bytes);
+ }
+
+ // Consumes an encoded message object containing FIDL encoded bytes and handles.
+ // The current buffer region in DecodedMessage is always released.
+ // Uses the FIDL encoding tables to deserialize the message in-place.
+ // If the message is invalid, discards the buffer and returns an error.
+ zx_status_t DecodeFrom(EncodedMessage<FidlType>* msg, const char** out_error_msg) {
+ // Clear any existing message.
+ CloseHandles();
+ bytes_ = BytePart();
+ zx_status_t status = fidl_decode(FidlType::type,
+ msg->bytes().data(), msg->bytes().actual(),
+ msg->handles().data(), msg->handles().actual(),
+ out_error_msg);
+ // Clear out |msg| independent of success or failure
+ BytePart bytes = msg->ReleaseBytesAndHandles();
+ if (status == ZX_OK) {
+ Reset(std::move(bytes));
+ } else {
+ Reset(BytePart());
+ }
+ return status;
+ }
+
+ // Serializes the content of the message in-place and stores the result
+ // in |out_msg|. The message's contents are always consumed by this
+ // operation, even in case of an error.
+ zx_status_t EncodeTo(EncodedMessage<FidlType>* out_msg, const char** out_error_msg) {
+ return out_msg->Initialize([this, &out_error_msg] (BytePart& msg_bytes,
+ HandlePart& msg_handles) {
+ msg_bytes = std::move(bytes_);
+ uint32_t actual_handles = 0;
+ zx_status_t status = fidl_encode(FidlType::type,
+ msg_bytes.data(), msg_bytes.actual(),
+ msg_handles.data(), msg_handles.capacity(),
+ &actual_handles, out_error_msg);
+ msg_handles.set_actual(actual_handles);
+ return status;
+ });
+ }
+
+ // Accesses the FIDL message by reinterpreting the buffer pointer.
+ // Returns nullptr if there is no message.
+ FidlType* message() const {
+ return reinterpret_cast<FidlType*>(bytes_.data());
+ }
+
+private:
+ // Use the FIDL encoding tables for |FidlType| to walk the message and
+ // destroy the handles it contains.
+ void CloseHandles() {
+#ifdef __Fuchsia__
+ if (bytes_.data()) {
+ fidl_close_handles(FidlType::type, bytes_.data(), bytes_.actual(), nullptr);
+ }
+#endif
+ }
+
+ // The contents of the decoded message.
+ BytePart bytes_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_LLCPP_DECODED_MESSAGE_H_
diff --git a/pkg/fidl/include/lib/fidl/llcpp/encoded_message.h b/pkg/fidl/include/lib/fidl/llcpp/encoded_message.h
new file mode 100644
index 0000000..8fd1a06
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/llcpp/encoded_message.h
@@ -0,0 +1,136 @@
+// Copyright 2018 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 LIB_FIDL_LLCPP_ENCODED_MESSAGE_H_
+#define LIB_FIDL_LLCPP_ENCODED_MESSAGE_H_
+
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
+#include <lib/fidl/cpp/message_part.h>
+#include <lib/fidl/llcpp/traits.h>
+#include <lib/fit/traits.h>
+#include <type_traits>
+#include <utility>
+#include <zircon/fidl.h>
+
+#ifdef __Fuchsia__
+#include <zircon/syscalls.h>
+#endif
+
+namespace fidl {
+
+// Holds an encoded FIDL message, that is, a byte array plus a handle table.
+//
+// The bytes part points to an external caller-managed buffer, while the handles part
+// is owned by this class. Any handles will be closed upon destruction.
+// This class is aware of the upper bound on the number of handles
+// in a message, such that its size can be adjusted to fit the demands
+// of a specific FIDL type.
+//
+// Because this class does not own the underlying message buffer, the caller
+// must make sure the lifetime of this class does not extend over that of the buffer.
+template <typename FidlType>
+class EncodedMessage final {
+ static_assert(IsFidlType<FidlType>::value, "Only FIDL types allowed here");
+ static_assert(FidlType::MaxSize > 0, "Positive message size");
+
+public:
+ // Instantiates an empty buffer with no bytes or handles.
+ EncodedMessage() = default;
+
+ EncodedMessage(EncodedMessage&& other) noexcept {
+ if (this != &other) {
+ MoveImpl(std::move(other));
+ }
+ }
+
+ EncodedMessage& operator=(EncodedMessage&& other) noexcept {
+ if (this != &other) {
+ MoveImpl(std::move(other));
+ }
+ return *this;
+ }
+
+ EncodedMessage(const EncodedMessage& other) = delete;
+
+ EncodedMessage& operator=(const EncodedMessage& other) = delete;
+
+ // Instantiates an EncodedMessage which points to a buffer region with caller-managed memory.
+ // It does not take ownership of that buffer region.
+ // Also initializes an empty handles part.
+ EncodedMessage(BytePart bytes) :
+ bytes_(std::move(bytes)) { }
+
+ ~EncodedMessage() {
+ CloseHandles();
+ }
+
+ // Takes ownership of the contents of the message.
+ // The bytes and handle parts will become empty, while the existing bytes part is returned.
+ // The caller is responsible for having transferred the handles elsewhere
+ // before calling this method.
+ BytePart ReleaseBytesAndHandles() {
+ handles_.set_actual(0);
+ BytePart return_bytes = std::move(bytes_);
+ return return_bytes;
+ }
+
+ const BytePart& bytes() const { return bytes_; }
+
+ const HandlePart& handles() const { return handles_; }
+
+ // Clears the contents of the EncodedMessage then invokes Callback
+ // to initialize the EncodedMessage in-place then returns the callback's
+ // result.
+ //
+ // |callback| is a callable object whose arguments are (BytePart&, HandlePart&).
+ template <typename Callback>
+ decltype(auto) Initialize(Callback callback) {
+ static_assert(
+ fit::callable_traits<Callback>::args::size == 2 &&
+ std::is_same<
+ BytePart&,
+ typename fit::template callable_traits<Callback>::args::template at<0>>::value &&
+ std::is_same<
+ HandlePart&,
+ typename fit::template callable_traits<Callback>::args::template at<1>>::value,
+ "Callback signature must be: T(BytePart&, HandlePart&).");
+ bytes_ = BytePart();
+ CloseHandles();
+ return callback(bytes_, handles_);
+ }
+
+private:
+ void CloseHandles() {
+ if (handles_.actual() > 0) {
+#ifdef __Fuchsia__
+ zx_handle_close_many(handles_.data(), handles_.actual());
+#else
+ // How did we have handles if not on Fuchsia? Something bad happened...
+ assert(false);
+#endif
+ handles_.set_actual(0);
+ }
+ }
+
+ void MoveImpl(EncodedMessage&& other) noexcept {
+ CloseHandles();
+ bytes_ = std::move(other.bytes_);
+ // copy handles from |other|
+ memcpy(handle_storage_, other.handle_storage_,
+ other.handles_.actual() * sizeof(zx_handle_t));
+ // release handles in |other|
+ handles_.set_actual(other.handles().actual());
+ other.handles_.set_actual(0);
+ }
+
+ BytePart bytes_;
+ zx_handle_t handle_storage_[FidlType::MaxNumHandles];
+ HandlePart handles_{handle_storage_, FidlType::MaxNumHandles};
+};
+
+}
+
+#endif // LIB_FIDL_LLCPP_ENCODED_MESSAGE_H_
diff --git a/pkg/fidl/include/lib/fidl/llcpp/traits.h b/pkg/fidl/include/lib/fidl/llcpp/traits.h
new file mode 100644
index 0000000..80fadd7
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/llcpp/traits.h
@@ -0,0 +1,67 @@
+// Copyright 2018 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 LIB_FIDL_LLCPP_TRAITS_H_
+#define LIB_FIDL_LLCPP_TRAITS_H_
+
+#include <lib/zx/object.h>
+#include <stdint.h>
+#include <type_traits>
+#include <zircon/fidl.h>
+
+// Defines type traits used in the low-level C++ binding.
+//
+// The contracts of a FIDL type |T| are as follows:
+//
+// |IsFidlType<T>| resolves to std::true_type.
+// |IsFidlMessage<T>| resolves to std::true_type iff |T| is a transactional message.
+// |T::MaxNumHandles| is an uint32_t specifying the upper bound on the number of contained handles.
+// |T::MaxSize| is an uint32_t specifying the upper bound on the message byte size.
+// It is std::numeric_limits<uint32_t>::max() if |T| is unbounded.
+// |T::type| is a fidl_type_t* pointing to the corresponding encoding table, if any.
+//
+
+namespace fidl {
+
+// A type trait that indicates whether the given type is a request/response type
+// i.e. has a FIDL message header.
+template <typename T> struct IsFidlMessage : public std::false_type {};
+
+// Code-gen will explicitly conform the generated FIDL transactional messages to IsFidlMessage.
+
+// A type trait that indicates whether the given type is allowed to appear in
+// generated binding APIs and can be encoded/decoded.
+// As a start, all handle types are supported.
+template <typename T> struct IsFidlType :
+ public std::is_base_of<zx::object_base, T> {};
+
+// clang-format off
+// Specialize for primitives
+template <> struct IsFidlType<bool> : public std::true_type {};
+template <> struct IsFidlType<uint8_t> : public std::true_type {};
+template <> struct IsFidlType<uint16_t> : public std::true_type {};
+template <> struct IsFidlType<uint32_t> : public std::true_type {};
+template <> struct IsFidlType<uint64_t> : public std::true_type {};
+template <> struct IsFidlType<int8_t> : public std::true_type {};
+template <> struct IsFidlType<int16_t> : public std::true_type {};
+template <> struct IsFidlType<int32_t> : public std::true_type {};
+template <> struct IsFidlType<int64_t> : public std::true_type {};
+template <> struct IsFidlType<float> : public std::true_type {};
+template <> struct IsFidlType<double> : public std::true_type {};
+// clang-format on
+
+// String
+class StringView;
+template <> struct IsFidlType<StringView> : public std::true_type {};
+
+// Vector (conditional on element)
+template <typename E> class VectorView;
+template <typename E>
+struct IsFidlType<VectorView<E>> : public IsFidlType<E> {};
+
+// Code-gen will explicitly conform the generated FIDL structures to IsFidlType.
+
+} // namespace fidl
+
+#endif // LIB_FIDL_LLCPP_TRAITS_H_
diff --git a/pkg/fidl/include/lib/fidl/transport.h b/pkg/fidl/include/lib/fidl/transport.h
new file mode 100644
index 0000000..2da0d9c
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/transport.h
@@ -0,0 +1,46 @@
+// Copyright 2018 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 LIB_FIDL_TRANSPORT_H_
+#define LIB_FIDL_TRANSPORT_H_
+
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+
+__BEGIN_CDECLS
+
+// Writes |capacity| bytes from |buffer| to the control channel of |socket|.
+//
+// Blocks until |socket| is able to accept a control plane message.
+zx_status_t fidl_socket_write_control(zx_handle_t socket, const void* buffer,
+ size_t capacity);
+
+// Reads |capacity| bytes from the control channel of |socket| to |buffer|.
+//
+// Blocks until a control plane message is able to be read from |socket|.
+//
+// The actual number of bytes reads from the control plan is returned in
+// |out_actual|.
+zx_status_t fidl_socket_read_control(zx_handle_t socket, void* buffer,
+ size_t capacity, size_t* out_actual);
+
+// Issues a transaction on the control channel of |socket|.
+//
+// First, writes |capacity| bytes from |buffer| to the control channel of
+// |socket|. Second, reads |out_capacity| bytes from the control channel of
+// |socket| to |out_buffer|.
+//
+// Blocks until the transaction is complete.
+//
+// |buffer| and |out_buffer| may be aliased.
+//
+// The actual number of bytes reads from the control plan is returned in
+// |out_actual|.
+zx_status_t fidl_socket_call_control(zx_handle_t socket, const void* buffer,
+ size_t capacity, void* out_buffer,
+ size_t out_capacity, size_t* out_actual);
+
+__END_CDECLS
+
+#endif // LIB_FIDL_TRANSPORT_H_
diff --git a/pkg/fidl/message.cpp b/pkg/fidl/message.cpp
new file mode 100644
index 0000000..6857459
--- /dev/null
+++ b/pkg/fidl/message.cpp
@@ -0,0 +1,117 @@
+// Copyright 2018 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.
+
+#include <lib/fidl/cpp/message.h>
+
+#include <string.h>
+
+#include <lib/fidl/coding.h>
+#include <lib/fidl/cpp/builder.h>
+
+#ifdef __Fuchsia__
+#include <zircon/syscalls.h>
+#endif
+
+namespace fidl {
+
+Message::Message() = default;
+
+Message::Message(BytePart bytes, HandlePart handles)
+ : bytes_(static_cast<BytePart&&>(bytes)),
+ handles_(static_cast<HandlePart&&>(handles)) {}
+
+Message::~Message() {
+#ifdef __Fuchsia__
+ zx_handle_close_many(handles_.data(), handles_.actual());
+#endif
+ ClearHandlesUnsafe();
+}
+
+Message::Message(Message&& other)
+ : bytes_(static_cast<BytePart&&>(other.bytes_)),
+ handles_(static_cast<HandlePart&&>(other.handles_)) {}
+
+Message& Message::operator=(Message&& other) {
+ bytes_ = static_cast<BytePart&&>(other.bytes_);
+ handles_ = static_cast<HandlePart&&>(other.handles_);
+ return *this;
+}
+
+zx_status_t Message::Encode(const fidl_type_t* type,
+ const char** error_msg_out) {
+ uint32_t actual_handles = 0u;
+ zx_status_t status = fidl_encode(type, bytes_.data(), bytes_.actual(),
+ handles_.data(), handles_.capacity(),
+ &actual_handles, error_msg_out);
+ if (status == ZX_OK)
+ handles_.set_actual(actual_handles);
+ return status;
+}
+
+zx_status_t Message::Decode(const fidl_type_t* type,
+ const char** error_msg_out) {
+ zx_status_t status = fidl_decode(type, bytes_.data(), bytes_.actual(),
+ handles_.data(), handles_.actual(),
+ error_msg_out);
+ ClearHandlesUnsafe();
+ return status;
+}
+
+zx_status_t Message::Validate(const fidl_type_t* type,
+ const char** error_msg_out) const {
+ return fidl_validate(type, bytes_.data(), bytes_.actual(),
+ handles_.actual(), error_msg_out);
+}
+
+#ifdef __Fuchsia__
+zx_status_t Message::Read(zx_handle_t channel, uint32_t flags) {
+ uint32_t actual_bytes = 0u;
+ uint32_t actual_handles = 0u;
+ zx_status_t status = zx_channel_read(
+ channel, flags, bytes_.data(), handles_.data(), bytes_.capacity(),
+ handles_.capacity(), &actual_bytes, &actual_handles);
+ if (status == ZX_OK) {
+ bytes_.set_actual(actual_bytes);
+ handles_.set_actual(actual_handles);
+ }
+ return status;
+}
+
+zx_status_t Message::Write(zx_handle_t channel, uint32_t flags) {
+ zx_status_t status = zx_channel_write(channel, flags, bytes_.data(),
+ bytes_.actual(), handles_.data(),
+ handles_.actual());
+ ClearHandlesUnsafe();
+ return status;
+}
+
+zx_status_t Message::Call(zx_handle_t channel, uint32_t flags,
+ zx_time_t deadline, Message* response) {
+ zx_channel_call_args_t args;
+ args.wr_bytes = bytes_.data();
+ args.wr_handles = handles_.data();
+ args.rd_bytes = response->bytes_.data();
+ args.rd_handles = response->handles_.data();
+ args.wr_num_bytes = bytes_.actual();
+ args.wr_num_handles = handles_.actual();
+ args.rd_num_bytes = response->bytes_.capacity();
+ args.rd_num_handles = response->handles_.capacity();
+ uint32_t actual_bytes = 0u;
+ uint32_t actual_handles = 0u;
+ zx_status_t status = zx_channel_call(channel, flags, deadline, &args,
+ &actual_bytes, &actual_handles);
+ ClearHandlesUnsafe();
+ if (status == ZX_OK) {
+ response->bytes_.set_actual(actual_bytes);
+ response->handles_.set_actual(actual_handles);
+ }
+ return status;
+}
+#endif
+
+void Message::ClearHandlesUnsafe() {
+ handles_.set_actual(0u);
+}
+
+} // namespace fidl
diff --git a/pkg/fidl/message_buffer.cpp b/pkg/fidl/message_buffer.cpp
new file mode 100644
index 0000000..54e2346
--- /dev/null
+++ b/pkg/fidl/message_buffer.cpp
@@ -0,0 +1,50 @@
+// Copyright 2018 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.
+
+#include <lib/fidl/cpp/message_buffer.h>
+#include <zircon/assert.h>
+
+#include <stdlib.h>
+
+namespace fidl {
+namespace {
+
+uint64_t AddPadding(uint32_t offset) {
+ constexpr uint32_t kMask = alignof(zx_handle_t) - 1;
+ // Cast before addition to avoid overflow.
+ return static_cast<uint64_t>(offset) + static_cast<uint64_t>(offset & kMask);
+}
+
+size_t GetAllocSize(uint32_t bytes_capacity, uint32_t handles_capacity) {
+ return AddPadding(bytes_capacity) + sizeof(zx_handle_t) * handles_capacity;
+}
+
+} // namespace
+
+MessageBuffer::MessageBuffer(uint32_t bytes_capacity,
+ uint32_t handles_capacity)
+ : buffer_(static_cast<uint8_t*>(malloc(GetAllocSize(bytes_capacity, handles_capacity)))),
+ bytes_capacity_(bytes_capacity),
+ handles_capacity_(handles_capacity) {
+ ZX_ASSERT_MSG(buffer_, "malloc returned NULL in MessageBuffer::MessageBuffer()");
+}
+
+MessageBuffer::~MessageBuffer() {
+ free(buffer_);
+}
+
+zx_handle_t* MessageBuffer::handles() const {
+ return reinterpret_cast<zx_handle_t*>(buffer_ + AddPadding(bytes_capacity_));
+}
+
+Message MessageBuffer::CreateEmptyMessage() {
+ return Message(BytePart(bytes(), bytes_capacity()),
+ HandlePart(handles(), handles_capacity()));
+}
+
+Builder MessageBuffer::CreateBuilder() {
+ return Builder(bytes(), bytes_capacity());
+}
+
+} // namespace fidl
diff --git a/pkg/fidl/message_builder.cpp b/pkg/fidl/message_builder.cpp
new file mode 100644
index 0000000..c178690
--- /dev/null
+++ b/pkg/fidl/message_builder.cpp
@@ -0,0 +1,35 @@
+// Copyright 2018 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.
+
+#include <lib/fidl/cpp/message_builder.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+namespace fidl {
+
+MessageBuilder::MessageBuilder(const fidl_type_t* type,
+ uint32_t bytes_capacity,
+ uint32_t handles_capacity)
+ : type_(type),
+ buffer_(bytes_capacity, handles_capacity) {
+ Reset();
+}
+
+MessageBuilder::~MessageBuilder() = default;
+
+zx_status_t MessageBuilder::Encode(Message* message_out,
+ const char** error_msg_out) {
+ *message_out = Message(Finalize(),
+ HandlePart(buffer_.handles(),
+ buffer_.handles_capacity()));
+ return message_out->Encode(type_, error_msg_out);
+}
+
+void MessageBuilder::Reset() {
+ Builder::Reset(buffer_.bytes(), buffer_.bytes_capacity());
+ New<fidl_message_header_t>();
+}
+
+} // namespace fidl
diff --git a/pkg/fidl/meta.json b/pkg/fidl/meta.json
new file mode 100644
index 0000000..be54c7d
--- /dev/null
+++ b/pkg/fidl/meta.json
@@ -0,0 +1,67 @@
+{
+ "banjo_deps": [],
+ "deps": [],
+ "fidl_deps": [],
+ "files": [
+ "pkg/fidl/buffer_walker.h",
+ "pkg/fidl/builder.cpp",
+ "pkg/fidl/decoding.cpp",
+ "pkg/fidl/encoding.cpp",
+ "pkg/fidl/epitaph.c",
+ "pkg/fidl/formatting.cpp",
+ "pkg/fidl/handle_closing.cpp",
+ "pkg/fidl/message.cpp",
+ "pkg/fidl/message_buffer.cpp",
+ "pkg/fidl/message_builder.cpp",
+ "pkg/fidl/transport.cpp",
+ "pkg/fidl/validating.cpp",
+ "pkg/fidl/include/lib/fidl/coding.h",
+ "pkg/fidl/include/lib/fidl/cpp/builder.h",
+ "pkg/fidl/include/lib/fidl/cpp/message.h",
+ "pkg/fidl/include/lib/fidl/cpp/message_buffer.h",
+ "pkg/fidl/include/lib/fidl/cpp/message_builder.h",
+ "pkg/fidl/include/lib/fidl/cpp/message_part.h",
+ "pkg/fidl/include/lib/fidl/cpp/string_view.h",
+ "pkg/fidl/include/lib/fidl/cpp/vector_view.h",
+ "pkg/fidl/include/lib/fidl/epitaph.h",
+ "pkg/fidl/include/lib/fidl/internal.h",
+ "pkg/fidl/include/lib/fidl/llcpp/decoded_message.h",
+ "pkg/fidl/include/lib/fidl/llcpp/encoded_message.h",
+ "pkg/fidl/include/lib/fidl/llcpp/traits.h",
+ "pkg/fidl/include/lib/fidl/transport.h"
+ ],
+ "headers": [
+ "pkg/fidl/include/lib/fidl/coding.h",
+ "pkg/fidl/include/lib/fidl/cpp/builder.h",
+ "pkg/fidl/include/lib/fidl/cpp/message.h",
+ "pkg/fidl/include/lib/fidl/cpp/message_buffer.h",
+ "pkg/fidl/include/lib/fidl/cpp/message_builder.h",
+ "pkg/fidl/include/lib/fidl/cpp/message_part.h",
+ "pkg/fidl/include/lib/fidl/cpp/string_view.h",
+ "pkg/fidl/include/lib/fidl/cpp/vector_view.h",
+ "pkg/fidl/include/lib/fidl/epitaph.h",
+ "pkg/fidl/include/lib/fidl/internal.h",
+ "pkg/fidl/include/lib/fidl/llcpp/decoded_message.h",
+ "pkg/fidl/include/lib/fidl/llcpp/encoded_message.h",
+ "pkg/fidl/include/lib/fidl/llcpp/traits.h",
+ "pkg/fidl/include/lib/fidl/transport.h"
+ ],
+ "include_dir": "pkg/fidl/include",
+ "name": "fidl",
+ "root": "pkg/fidl",
+ "sources": [
+ "pkg/fidl/buffer_walker.h",
+ "pkg/fidl/builder.cpp",
+ "pkg/fidl/decoding.cpp",
+ "pkg/fidl/encoding.cpp",
+ "pkg/fidl/epitaph.c",
+ "pkg/fidl/formatting.cpp",
+ "pkg/fidl/handle_closing.cpp",
+ "pkg/fidl/message.cpp",
+ "pkg/fidl/message_buffer.cpp",
+ "pkg/fidl/message_builder.cpp",
+ "pkg/fidl/transport.cpp",
+ "pkg/fidl/validating.cpp"
+ ],
+ "type": "cc_source_library"
+}
\ No newline at end of file
diff --git a/pkg/fidl/transport.cpp b/pkg/fidl/transport.cpp
new file mode 100644
index 0000000..253b5ec
--- /dev/null
+++ b/pkg/fidl/transport.cpp
@@ -0,0 +1,70 @@
+// Copyright 2018 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.
+
+#ifdef __Fuchsia__
+
+#include <lib/fidl/transport.h>
+#include <zircon/assert.h>
+#include <zircon/syscalls.h>
+
+zx_status_t fidl_socket_write_control(zx_handle_t socket, const void* buffer,
+ size_t capacity) {
+ for (;;) {
+ zx_status_t status = zx_socket_write(socket, ZX_SOCKET_CONTROL, buffer,
+ capacity, nullptr);
+ if (status != ZX_ERR_SHOULD_WAIT) {
+ return status;
+ }
+
+ zx_signals_t observed = ZX_SIGNAL_NONE;
+ status = zx_object_wait_one(socket, ZX_SOCKET_CONTROL_WRITABLE | ZX_SOCKET_PEER_CLOSED,
+ ZX_TIME_INFINITE, &observed);
+ if (status != ZX_OK) {
+ return status;
+ }
+
+ if (observed & ZX_SOCKET_PEER_CLOSED) {
+ return ZX_ERR_PEER_CLOSED;
+ }
+
+ ZX_ASSERT(observed & ZX_SOCKET_CONTROL_WRITABLE);
+ }
+}
+
+zx_status_t fidl_socket_read_control(zx_handle_t socket, void* buffer,
+ size_t capacity, size_t* out_actual) {
+ for (;;) {
+ zx_status_t status = zx_socket_read(socket, ZX_SOCKET_CONTROL, buffer,
+ capacity, out_actual);
+ if (status != ZX_ERR_SHOULD_WAIT) {
+ return status;
+ }
+
+ zx_signals_t observed = ZX_SIGNAL_NONE;
+ status = zx_object_wait_one(socket, ZX_SOCKET_CONTROL_READABLE | ZX_SOCKET_PEER_CLOSED,
+ ZX_TIME_INFINITE, &observed);
+ if (status != ZX_OK) {
+ return status;
+ }
+
+ if (observed & ZX_SOCKET_CONTROL_READABLE) {
+ continue;
+ }
+
+ ZX_ASSERT(observed & ZX_SOCKET_PEER_CLOSED);
+ return ZX_ERR_PEER_CLOSED;
+ }
+}
+
+zx_status_t fidl_socket_call_control(zx_handle_t socket, const void* buffer,
+ size_t capacity, void* out_buffer,
+ size_t out_capacity, size_t* out_actual) {
+ zx_status_t status = fidl_socket_write_control(socket, buffer, capacity);
+ if (status != ZX_OK) {
+ return status;
+ }
+ return fidl_socket_read_control(socket, out_buffer, out_capacity, out_actual);
+}
+
+#endif
diff --git a/pkg/fidl/validating.cpp b/pkg/fidl/validating.cpp
new file mode 100644
index 0000000..342face
--- /dev/null
+++ b/pkg/fidl/validating.cpp
@@ -0,0 +1,90 @@
+// 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.
+
+#include <lib/fidl/coding.h>
+
+#include <stdalign.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <lib/fidl/internal.h>
+#include <zircon/assert.h>
+#include <zircon/compiler.h>
+
+#include "buffer_walker.h"
+
+// TODO(kulakowski) Design zx_status_t error values.
+
+namespace {
+
+class FidlValidator final : public fidl::internal::BufferWalker<FidlValidator, false, false> {
+ typedef fidl::internal::BufferWalker<FidlValidator, false, false> Super;
+
+public:
+ FidlValidator(const fidl_type_t* type, const void* bytes, uint32_t num_bytes,
+ uint32_t num_handles, const char** out_error_msg)
+ : Super(type), bytes_(static_cast<const uint8_t*>(bytes)), num_bytes_(num_bytes),
+ num_handles_(num_handles), out_error_msg_(out_error_msg) {}
+
+ void Walk() {
+ Super::Walk();
+ if (status_ == ZX_OK && handle_idx() != num_handles()) {
+ SetError("message did not contain the specified number of handles");
+ return;
+ }
+ }
+
+ const uint8_t* bytes() const { return bytes_; }
+ uint32_t num_bytes() const { return num_bytes_; }
+ uint32_t num_handles() const { return num_handles_; }
+
+ bool ValidateOutOfLineStorageClaim(const void* a, const void* b) {
+ return true;
+ }
+
+ void UnclaimedHandle(const zx_handle_t* out_handle) {}
+ void ClaimedHandle(const zx_handle_t* out_handle, uint32_t idx) {}
+
+ template <class T>
+ void UpdatePointer(const T* const* p, const T* v) {}
+
+ PointerState GetPointerState(const void* ptr) const {
+ return static_cast<PointerState>(*static_cast<const uintptr_t*>(ptr));
+ }
+ HandleState GetHandleState(zx_handle_t p) const {
+ return static_cast<HandleState>(p);
+ }
+
+ void SetError(const char* error_msg) {
+ status_ = ZX_ERR_INVALID_ARGS;
+ if (out_error_msg_ != nullptr) {
+ *out_error_msg_ = error_msg;
+ }
+ }
+
+ zx_status_t status() const { return status_; }
+
+private:
+ // Message state passed in to the constructor.
+ const uint8_t* const bytes_;
+ const uint32_t num_bytes_;
+ const uint32_t num_handles_;
+ const char** const out_error_msg_;
+ zx_status_t status_ = ZX_OK;
+};
+
+} // namespace
+
+zx_status_t fidl_validate(const fidl_type_t* type, const void* bytes, uint32_t num_bytes,
+ uint32_t num_handles, const char** out_error_msg) {
+ FidlValidator validator(type, bytes, num_bytes, num_handles, out_error_msg);
+ validator.Walk();
+ return validator.status();
+}
+
+zx_status_t fidl_validate_msg(const fidl_type_t* type, const fidl_msg_t* msg,
+ const char** out_error_msg) {
+ return fidl_validate(type, msg->bytes, msg->num_bytes, msg->num_handles,
+ out_error_msg);
+}