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);
+}