[fidl] Implement validator using visitor.

At this point, the migration from buffer_walker to visitor is complete.
There should be no external behavioral change.

TEST: CQ, and tryjobs in higher layers
Change-Id: I6859316f03b9cd61e17f1374b003741cd2caaff1
diff --git a/system/ulib/fidl/buffer_walker.h b/system/ulib/fidl/buffer_walker.h
deleted file mode 100644
index 93d9484..0000000
--- a/system/ulib/fidl/buffer_walker.h
+++ /dev/null
@@ -1,976 +0,0 @@
-// Copyright 2017 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ZIRCON_SYSTEM_ULIB_FIDL_BUFFER_WALKER_H_
-#define ZIRCON_SYSTEM_ULIB_FIDL_BUFFER_WALKER_H_
-
-#include <lib/fidl/internal.h>
-#include <cstdlib>
-
-#include <cstdint>
-#include <cstdlib>
-#include <stdalign.h>
-#include <type_traits>
-
-#include <lib/fidl/coding.h>
-#include <lib/fidl/internal.h>
-#include <zircon/assert.h>
-#include <zircon/compiler.h>
-
-namespace fidl {
-namespace internal {
-
-// Some assumptions about data type layout.
-static_assert(offsetof(fidl_string_t, size) == 0u, "");
-static_assert(offsetof(fidl_string_t, data) == 8u, "");
-
-static_assert(offsetof(fidl_vector_t, count) == 0u, "");
-static_assert(offsetof(fidl_vector_t, data) == 8u, "");
-
-static_assert(ZX_HANDLE_INVALID == FIDL_HANDLE_ABSENT, "");
-
-template <bool kConst, class U>
-struct SetPtrConst;
-
-template <class U>
-struct SetPtrConst<false, U> {
-    typedef U* type;
-};
-
-template <class U>
-struct SetPtrConst<true, U> {
-    typedef const U* type;
-};
-
-// Walks over a FIDL buffer and validates/encodes/decodes it per-Derived.
-//
-// kMutating controls whether this deals with mutable bytes or immutable bytes
-// (validation wants immutable, encode/decode wants mutable)
-//
-// kContinueAfterErrors controls whether parsing is continued upon failure (encode needs this to
-// see all available handles).
-//
-// Derived should offer the following methods:
-//
-//   const? uint8_t* bytes() - returns the start of the buffer of bytes
-//   uint32_t num_bytes() - returns the number of bytes in said buffer
-//   uint32_t num_handles() - returns the number of handles that are claimable
-//   bool ValidateOutOfLineStorageClaim(const void* a, const void* b)
-//      - returns true if a legally points to b
-//   void UnclaimedHandle(zx_handle_t*) - notes that a handle was skipped
-//   void ClaimedHandle(zx_handle_t*, uint32_t idx) - notes that a handle was claimed
-//   PointerState GetPointerState(const void* ptr) - returns whether a pointer is present or not
-//   HandleState GetHandleState(zx_handle_t) - returns if a handle is present or not
-//   void UpdatePointer(T**p, T*v) - mutates a pointer representation for a present pointer
-//   void SetError(const char* error_msg) - flags that an error occurred
-template <class Derived, bool kMutating, bool kContinueAfterErrors>
-class BufferWalker {
-public:
-    explicit BufferWalker(const fidl_type* type)
-        : type_(type) {}
-
-    void Walk() {
-        // The first decode is special. It must be a struct or a table.
-        // We need to know the size of the first element to compute the start of
-        // the out-of-line allocations.
-
-        if (type_ == nullptr) {
-            SetError("Cannot decode a null fidl type");
-            return;
-        }
-
-        if (bytes() == nullptr) {
-            SetError("Cannot decode null bytes");
-            return;
-        }
-
-        switch (type_->type_tag) {
-        case fidl::kFidlTypeStruct:
-            if (num_bytes() < type_->coded_struct.size) {
-                SetError("Message size is smaller than expected");
-                return;
-            }
-            out_of_line_offset_ = static_cast<uint32_t>(fidl::FidlAlign(type_->coded_struct.size));
-            break;
-        case fidl::kFidlTypeTable:
-            if (num_bytes() < sizeof(fidl_vector_t)) {
-                SetError("Message size is smaller than expected");
-                return;
-            }
-            out_of_line_offset_ = static_cast<uint32_t>(fidl::FidlAlign(sizeof(fidl_vector_t)));
-            break;
-        default:
-            SetError("Message must be a struct or a table");
-            return;
-        }
-
-        Push(Frame::DoneSentinel());
-        Push(Frame(type_, 0u));
-
-// Macro to insert the relevant goop required to support two control flows here:
-// one where we keep reading after error, and another where we return immediately.
-// No runtime overhead thanks to if constexpr magic.
-#define FIDL_POP_AND_CONTINUE_OR_RETURN \
-    if (kContinueAfterErrors) {         \
-        Pop();                          \
-        continue;                       \
-    } else {                            \
-        return;                         \
-    }
-
-        for (;;) {
-            Frame* frame = Peek();
-
-            switch (frame->state) {
-            case Frame::kStateStruct: {
-                const uint32_t field_index = frame->NextStructField();
-                if (field_index == frame->struct_state.field_count) {
-                    Pop();
-                    continue;
-                }
-                const fidl::FidlStructField& field = frame->struct_state.fields[field_index];
-                const fidl_type_t* field_type = field.type;
-                const uint32_t field_offset = frame->offset + field.offset;
-                if (!Push(Frame(field_type, field_offset))) {
-                    SetError("recursion depth exceeded processing struct");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                continue;
-            }
-            case Frame::kStateStructPointer: {
-                switch (GetPointerState(TypedAt<void>(frame->offset))) {
-                case PointerState::PRESENT:
-                    break;
-                case PointerState::ABSENT:
-                    Pop();
-                    continue;
-                default:
-                    SetError("Tried to decode a bad struct pointer");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                auto struct_ptr_ptr = TypedAt<void*>(frame->offset);
-                if (!ClaimOutOfLineStorage(frame->struct_pointer_state.struct_type->size,
-                                           *struct_ptr_ptr, &frame->offset)) {
-                    SetError("message wanted to store too large of a nullable struct");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                UpdatePointer(struct_ptr_ptr, TypedAt<void>(frame->offset));
-                const fidl::FidlCodedStruct* coded_struct = frame->struct_pointer_state.struct_type;
-                *frame = Frame(coded_struct, frame->offset);
-                continue;
-            }
-            case Frame::kStateTable: {
-                if (frame->field == 0u) {
-                    auto envelope_vector_ptr = TypedAt<fidl_vector_t>(frame->offset);
-                    switch (GetPointerState(&envelope_vector_ptr->data)) {
-                    case PointerState::PRESENT:
-                        break;
-                    case PointerState::ABSENT:
-                        SetError("Table data cannot be absent");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    default:
-                        SetError("message tried to decode a non-present vector");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-                    uint32_t size;
-                    if (mul_overflow(envelope_vector_ptr->count, 2 * sizeof(uint64_t), &size)) {
-                        SetError("integer overflow calculating table size");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-                    if (!ClaimOutOfLineStorage(size, envelope_vector_ptr->data, &frame->offset)) {
-                        SetError("message wanted to store too large of a table");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-                    UpdatePointer(&envelope_vector_ptr->data, TypedAt<void>(frame->offset));
-                    frame->field = 1;
-                    frame->table_state.known_index = 0;
-                    frame->table_state.present_count = static_cast<uint32_t>(envelope_vector_ptr->count);
-                    frame->table_state.end_offset = out_of_line_offset_;
-                    frame->table_state.end_handle = handle_idx_;
-                    continue;
-                }
-                if (frame->table_state.end_offset != out_of_line_offset_) {
-                    SetError("Table field was mis-sized");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                if (frame->table_state.end_handle != handle_idx_) {
-                    SetError("Table handles were mis-sized");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                if (frame->field > frame->table_state.present_count) {
-                    Pop();
-                    continue;
-                }
-                const fidl::FidlTableField* known_field = nullptr;
-                if (frame->table_state.known_index < frame->table_state.field_count) {
-                    const fidl::FidlTableField* field =
-                        &frame->table_state.fields[frame->table_state.known_index];
-                    if (field->ordinal == frame->field) {
-                        known_field = field;
-                        frame->table_state.known_index++;
-                    }
-                }
-                const uint32_t envelope_hdr_offset = static_cast<uint32_t>(
-                    frame->offset + (frame->field - 1) * 2 * sizeof(uint64_t));
-                const uint32_t data_offset = static_cast<uint32_t>(
-                    envelope_hdr_offset + sizeof(uint64_t));
-                const uint64_t packed_sizes = *TypedAt<uint64_t>(envelope_hdr_offset);
-                frame->field++;
-                switch (GetPointerState(TypedAt<void>(data_offset))) {
-                case PointerState::PRESENT:
-                    if (packed_sizes != 0)
-                        break; // expected
-
-                    SetError("Table envelope has present data pointer, but no data, and no handles");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                case PointerState::ABSENT:
-                    if (packed_sizes == 0)
-                        continue; // skip
-
-                    SetError("Table envelope has absent data pointer, yet has data and/or handles");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                default:
-                    SetError("Table envelope has bad data pointer");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                uint32_t offset;
-                uint32_t handles;
-                const uint32_t table_bytes = static_cast<uint32_t>(packed_sizes & 0xffffffffu);
-                const uint32_t table_handles = static_cast<uint32_t>(packed_sizes >> 32);
-                if (add_overflow(out_of_line_offset_, table_bytes, &offset) || offset > num_bytes()) {
-                    SetError("table is larger than expected");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                if (add_overflow(handle_idx_, table_handles, &handles) ||
-                    handles > num_handles()) {
-                    SetError("table has more handles than expected");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                frame->table_state.end_offset = offset;
-                frame->table_state.end_handle = handles;
-                if (known_field != nullptr) {
-                    const fidl_type_t* field_type = known_field->type;
-                    uint32_t field_offset;
-                    if (!ClaimOutOfLineStorage(TypeSize(field_type), TypedAt<void*>(data_offset), &field_offset)) {
-                        SetError("table wanted too many bytes in field");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-                    UpdatePointer(TypedAt<void*>(data_offset), TypedAt<void>(field_offset));
-                    if (!Push(Frame(field_type, field_offset))) {
-                        SetError("recursion depth exceeded decoding table");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-                } else {
-                    // Table data will not be processed: discard it.
-                    uint32_t field_offset;
-                    if (!ClaimOutOfLineStorage(table_bytes, TypedAt<void*>(data_offset), &field_offset)) {
-                        SetError("table wanted too many bytes in field");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-                    UpdatePointer(TypedAt<void*>(data_offset), TypedAt<void>(field_offset));
-                    for (uint32_t i = 0; i < table_handles; i++) {
-                        if (!ClaimHandle(nullptr)) {
-                            SetError("expected handle not present");
-                            FIDL_POP_AND_CONTINUE_OR_RETURN;
-                        }
-                    }
-                }
-                continue;
-            }
-            case Frame::kStateTablePointer: {
-                switch (GetPointerState(TypedAt<void>(frame->offset))) {
-                case PointerState::PRESENT:
-                    break;
-                case PointerState::ABSENT:
-                    Pop();
-                    continue;
-                default:
-                    SetError("Tried to decode a bad table pointer");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                auto table_ptr_ptr = TypedAt<void*>(frame->offset);
-                if (!ClaimOutOfLineStorage(sizeof(fidl_vector_t), *table_ptr_ptr, &frame->offset)) {
-                    SetError("message wanted to store too large of a nullable table");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                UpdatePointer(table_ptr_ptr, TypedAt<void>(frame->offset));
-                const fidl::FidlCodedTable* coded_table = frame->table_pointer_state.table_type;
-                *frame = Frame(coded_table, frame->offset);
-                continue;
-            }
-            case Frame::kStateXUnion: {
-                auto xunion = TypedAt<fidl_xunion_t>(frame->offset);
-
-                if (xunion->padding != 0) {
-                    SetError("xunion padding after discriminant are non-zero");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-
-                uint32_t end_offset;
-                if (add_overflow(out_of_line_offset_, xunion->envelope.num_bytes, &end_offset) || end_offset > num_bytes()) {
-                    SetError("xunion size is larger than expected");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-
-                uint32_t total_handle_count;
-                if (add_overflow(handle_idx_, xunion->envelope.num_handles, &total_handle_count) ||
-                    total_handle_count > num_handles()) {
-                    SetError("xunion has more handles than expected");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-
-                switch (GetPointerState(&xunion->envelope.data)) {
-                case PointerState::PRESENT:
-                    if (xunion->tag == 0) {
-                        SetError("xunion has zero discriminant but envelope is present");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-                    break;
-                case PointerState::ABSENT:
-                    if (xunion->tag != 0) {
-                        SetError("xunion has non-zero discriminant but no data");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-
-                    // Empty xunion.
-                    frame->offset += static_cast<uint32_t>(sizeof(fidl_xunion_t));
-                    Pop();
-                    continue;
-                case PointerState::INVALID:
-                default:
-                    SetError("xunion has invalid envelope pointer");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    break;
-                }
-
-                const FidlXUnionField* known_field = nullptr;
-                for (size_t i = 0; i < frame->xunion_state.field_count; i++) {
-                    const auto field = frame->xunion_state.fields + i;
-
-                    if (field->ordinal == xunion->tag) {
-                        known_field = field;
-                        break;
-                    }
-                }
-
-                auto claim_data = [&](uint32_t known_size, uint32_t* envelope_offset) {
-                    if (!ClaimOutOfLineStorage(known_size, xunion->envelope.data, envelope_offset)) {
-                        SetError("xunion out-of-line storage claim couldn't be validated");
-                        return false;
-                    }
-
-                    if (*envelope_offset + xunion->envelope.num_bytes != end_offset) {
-                        SetError("expected xunion end offset doesn't match envelope offset + recursive size");
-                        return false;
-                    }
-
-                    UpdatePointer(&xunion->envelope.data, TypedAt<void>(*envelope_offset));
-
-                    return true;
-                };
-
-                uint32_t envelope_offset;
-                if (known_field != nullptr) {
-                    if (!claim_data(TypeSize(known_field->type), &envelope_offset)) {
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-
-                    frame->offset = envelope_offset;
-                    *frame = Frame(known_field->type, envelope_offset);
-                } else {
-                    if (!claim_data(xunion->envelope.num_bytes, &envelope_offset)) {
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-
-                    for (uint32_t i = 0; i < xunion->envelope.num_handles; i++) {
-                        if (!ClaimHandle(nullptr)) {
-                            SetError("expected handle not present");
-                            FIDL_POP_AND_CONTINUE_OR_RETURN;
-                        }
-                    }
-
-                    frame->offset = end_offset;
-                    Pop();
-                }
-
-                continue;
-            }
-            case Frame::kStateXUnionPointer: {
-                auto xunion_ptr_ptr = TypedAt<fidl_xunion_tag_t*>(frame->offset);
-                switch (GetPointerState(TypedAt<void>(frame->offset))) {
-                case PointerState::PRESENT:
-                    break;
-                case PointerState::ABSENT:
-                    Pop();
-                    continue;
-                default:
-                    SetError("Tried to decode a bad xunion pointer");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                if (!ClaimOutOfLineStorage(sizeof(fidl_xunion_t), *xunion_ptr_ptr,
-                                           &frame->offset)) {
-                    SetError("message wanted to store too large of a nullable xunion");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                UpdatePointer(xunion_ptr_ptr, TypedAt<fidl_xunion_tag_t>(frame->offset));
-                const fidl::FidlCodedXUnion* coded_xunion = frame->xunion_pointer_state.xunion_type;
-                *frame = Frame(coded_xunion, frame->offset);
-                continue;
-            }
-            case Frame::kStateUnion: {
-                fidl_union_tag_t union_tag = *TypedAt<fidl_union_tag_t>(frame->offset);
-                if (union_tag >= frame->union_state.type_count) {
-                    SetError("Tried to decode a bad union discriminant");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                const fidl_type_t* member = frame->union_state.types[union_tag];
-                if (!member) {
-                    Pop();
-                    continue;
-                }
-                frame->offset += frame->union_state.data_offset;
-                *frame = Frame(member, frame->offset);
-                continue;
-            }
-            case Frame::kStateUnionPointer: {
-                auto union_ptr_ptr = TypedAt<fidl_union_tag_t*>(frame->offset);
-                switch (GetPointerState(TypedAt<void>(frame->offset))) {
-                case PointerState::PRESENT:
-                    break;
-                case PointerState::ABSENT:
-                    Pop();
-                    continue;
-                default:
-                    SetError("Tried to decode a bad union pointer");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                if (!ClaimOutOfLineStorage(frame->union_pointer_state.union_type->size, *union_ptr_ptr,
-                                           &frame->offset)) {
-                    SetError("message wanted to store too large of a nullable union");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                UpdatePointer(union_ptr_ptr, TypedAt<fidl_union_tag_t>(frame->offset));
-                const fidl::FidlCodedUnion* coded_union = frame->union_pointer_state.union_type;
-                *frame = Frame(coded_union, frame->offset);
-                continue;
-            }
-            case Frame::kStateArray: {
-                const uint32_t element_offset = frame->NextArrayOffset();
-                if (element_offset == frame->array_state.array_size) {
-                    Pop();
-                    continue;
-                }
-                const fidl_type_t* element_type = frame->array_state.element;
-                const uint32_t offset = frame->offset + element_offset;
-                if (!Push(Frame(element_type, offset))) {
-                    SetError("recursion depth exceeded decoding array");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                continue;
-            }
-            case Frame::kStateString: {
-                auto string_ptr = TypedAt<fidl_string_t>(frame->offset);
-                // The string storage may be Absent for nullable strings and must
-                // otherwise be Present. No other values are allowed.
-                switch (GetPointerState(&string_ptr->data)) {
-                case PointerState::PRESENT:
-                    break;
-                case PointerState::ABSENT:
-                    if (!frame->string_state.nullable) {
-                        SetError("message tried to decode an absent non-nullable string");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-                    if (string_ptr->size != 0u) {
-                        SetError("message tried to decode an absent string of non-zero length");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-                    Pop();
-                    continue;
-                default:
-                    SetError(
-                        "message tried to decode a string that is neither present nor absent");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                uint64_t bound = frame->string_state.max_size;
-                uint64_t size = string_ptr->size;
-                if (size > bound) {
-                    SetError("message tried to decode too large of a bounded string");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                uint32_t string_data_offset = 0u;
-                if (!ClaimOutOfLineStorage(static_cast<uint32_t>(size), string_ptr->data, &string_data_offset)) {
-                    SetError("decoding a string overflowed buffer");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                UpdatePointer(&string_ptr->data, TypedAt<char>(string_data_offset));
-                Pop();
-                continue;
-            }
-            case Frame::kStateHandle: {
-                auto handle_ptr = TypedAt<zx_handle_t>(frame->offset);
-                // The handle storage may be Absent for nullable handles and must
-                // otherwise be Present. No other values are allowed.
-                switch (GetHandleState(*handle_ptr)) {
-                case HandleState::ABSENT:
-                    if (frame->handle_state.nullable) {
-                        Pop();
-                        continue;
-                    }
-                    SetError("message tried to decode a non-present handle");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                case HandleState::PRESENT:
-                    if (!ClaimHandle(handle_ptr)) {
-                        SetError("message decoded too many handles");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-                    Pop();
-                    continue;
-                case HandleState::INVALID:
-                default: {
-                    // The value at the handle was garbage.
-                    SetError("message tried to decode a garbage handle");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                }
-            }
-            case Frame::kStateVector: {
-                auto vector_ptr = TypedAt<fidl_vector_t>(frame->offset);
-                // The vector storage may be Absent for nullable vectors and must
-                // otherwise be Present. No other values are allowed.
-                switch (GetPointerState(&vector_ptr->data)) {
-                case PointerState::PRESENT:
-                    break;
-                case PointerState::ABSENT:
-                    if (!frame->vector_state.nullable) {
-                        SetError("message tried to decode an absent non-nullable vector");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-                    if (vector_ptr->count != 0u) {
-                        SetError("message tried to decode an absent vector of non-zero elements");
-                        FIDL_POP_AND_CONTINUE_OR_RETURN;
-                    }
-                    Pop();
-                    continue;
-                default:
-                    SetError("message tried to decode a non-present vector");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                if (vector_ptr->count > frame->vector_state.max_count) {
-                    SetError("message tried to decode too large of a bounded vector");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                uint32_t size;
-                if (mul_overflow(vector_ptr->count, frame->vector_state.element_size, &size)) {
-                    SetError("integer overflow calculating vector size");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                if (!ClaimOutOfLineStorage(size, vector_ptr->data, &frame->offset)) {
-                    SetError("message wanted to store too large of a vector");
-                    FIDL_POP_AND_CONTINUE_OR_RETURN;
-                }
-                UpdatePointer(&vector_ptr->data, TypedAt<void>(frame->offset));
-                if (frame->vector_state.element) {
-                    // Continue by decoding the vector elements as an array.
-                    *frame = Frame(frame->vector_state.element, size,
-                                   frame->vector_state.element_size, frame->offset);
-                } else {
-                    // If there is no element type pointer, there is
-                    // nothing to decode in the vector secondary
-                    // payload. So just continue.
-                    Pop();
-                }
-                continue;
-            }
-            case Frame::kStateDone: {
-                if (out_of_line_offset_ != num_bytes()) {
-                    SetError("message did not decode all provided bytes");
-                }
-                return;
-            }
-            }
-        }
-
-#undef FIDL_POP_AND_CONTINUE_OR_RETURN
-    }
-
-protected:
-    void SetError(const char* error_msg) {
-        derived()->SetError(error_msg);
-    }
-
-    template <typename T>
-    typename SetPtrConst<!kMutating, T>::type TypedAt(uint32_t offset) const {
-        return reinterpret_cast<typename SetPtrConst<!kMutating, T>::type>(bytes() + offset);
-    }
-
-    enum class PointerState : uintptr_t {
-        PRESENT = FIDL_ALLOC_PRESENT,
-        ABSENT = FIDL_ALLOC_ABSENT,
-        INVALID = 1 // *OR* *ANY* non PRESENT/ABSENT value.
-    };
-
-    enum class HandleState : zx_handle_t {
-        PRESENT = FIDL_HANDLE_PRESENT,
-        ABSENT = FIDL_HANDLE_ABSENT,
-        INVALID = 1 // *OR* *ANY* non PRESENT/ABSENT value.
-    };
-
-    uint32_t handle_idx() const { return handle_idx_; }
-
-private:
-    Derived* derived() {
-        return static_cast<Derived*>(this);
-    }
-
-    const Derived* derived() const {
-        return static_cast<const Derived*>(this);
-    }
-
-    // Returns a pointer to the bytes in the message.
-    auto bytes() const {
-        return derived()->bytes();
-    }
-
-    // Returns the number of bytes in the message.
-    auto num_bytes() const {
-        return derived()->num_bytes();
-    }
-
-    // Returns the number of handles in the message (encoding: the max number of handles in the message).
-    auto num_handles() const {
-        return derived()->num_handles();
-    }
-
-    // Returns PRESENT/ABSENT/INVALID for a given pointer value.
-    PointerState GetPointerState(const void* ptr) const {
-        return derived()->GetPointerState(ptr);
-    }
-
-    // Returns PRESENT/ABSENT/INVALID for a given handle value.
-    HandleState GetHandleState(zx_handle_t p) const {
-        return derived()->GetHandleState(p);
-    }
-
-    // If required: mutate a pointer to the dual representation.
-    template <class T2, class T1>
-    void UpdatePointer(T2 p, T1 v) {
-        derived()->UpdatePointer(p, v);
-    }
-
-    // Returns true when a handle was claimed, and false when the
-    // handles are exhausted.
-    template <class ZxHandleTPointer>
-    bool ClaimHandle(ZxHandleTPointer out_handle) {
-        if (handle_idx_ == num_handles()) {
-            derived()->UnclaimedHandle(out_handle);
-            return false;
-        }
-        derived()->ClaimedHandle(out_handle, handle_idx_);
-        ++handle_idx_;
-        return true;
-    }
-
-    // Returns true when the buffer space is claimed, and false when
-    // the requested claim is too large for bytes_.
-    bool ClaimOutOfLineStorage(uint32_t size, const void* storage, uint32_t* out_offset) {
-        if (!derived()->ValidateOutOfLineStorageClaim(storage, &bytes()[out_of_line_offset_])) {
-            return false;
-        }
-
-        // We have to manually maintain alignment here. For example, a pointer
-        // to a struct that is 4 bytes still needs to advance the next
-        // out-of-line offset by 8 to maintain the aligned-to-FIDL_ALIGNMENT
-        // property.
-        static constexpr uint32_t mask = FIDL_ALIGNMENT - 1;
-        uint32_t offset = out_of_line_offset_;
-        if (add_overflow(offset, size, &offset) ||
-            add_overflow(offset, mask, &offset)) {
-            return false;
-        }
-        offset &= ~mask;
-
-        if (offset > num_bytes()) {
-            return false;
-        }
-        *out_offset = out_of_line_offset_;
-        out_of_line_offset_ = offset;
-        return true;
-    }
-
-    uint32_t TypeSize(const fidl_type_t* type) {
-        switch (type->type_tag) {
-        case fidl::kFidlTypeStructPointer:
-        case fidl::kFidlTypeTablePointer:
-        case fidl::kFidlTypeUnionPointer:
-        case fidl::kFidlTypeXUnionPointer:
-            return sizeof(uint64_t);
-        case fidl::kFidlTypeHandle:
-            return sizeof(zx_handle_t);
-        case fidl::kFidlTypeStruct:
-            return type->coded_struct.size;
-        case fidl::kFidlTypeTable:
-            return sizeof(fidl_vector_t);
-        case fidl::kFidlTypeUnion:
-            return type->coded_union.size;
-        case fidl::kFidlTypeXUnion:
-            return sizeof(fidl_xunion_t);
-        case fidl::kFidlTypeString:
-            return sizeof(fidl_string_t);
-        case fidl::kFidlTypeArray:
-            return type->coded_array.array_size;
-        case fidl::kFidlTypeVector:
-            return sizeof(fidl_vector_t);
-        }
-        abort();
-        return 0;
-    }
-
-    // Functions that manipulate the decoding stack frames.
-    struct Frame {
-        Frame(const fidl_type_t* fidl_type, uint32_t offset)
-            : offset(offset) {
-            switch (fidl_type->type_tag) {
-            case fidl::kFidlTypeStruct:
-                state = kStateStruct;
-                struct_state.fields = fidl_type->coded_struct.fields;
-                struct_state.field_count = fidl_type->coded_struct.field_count;
-                break;
-            case fidl::kFidlTypeStructPointer:
-                state = kStateStructPointer;
-                struct_pointer_state.struct_type = fidl_type->coded_struct_pointer.struct_type;
-                break;
-            case fidl::kFidlTypeTable:
-                state = kStateTable;
-                table_state.fields = fidl_type->coded_table.fields;
-                table_state.field_count = fidl_type->coded_table.field_count;
-                table_state.present_count = 0;
-                break;
-            case fidl::kFidlTypeTablePointer:
-                state = kStateTablePointer;
-                table_pointer_state.table_type = fidl_type->coded_table_pointer.table_type;
-                break;
-            case fidl::kFidlTypeUnion:
-                state = kStateUnion;
-                union_state.types = fidl_type->coded_union.types;
-                union_state.type_count = fidl_type->coded_union.type_count;
-                union_state.data_offset = fidl_type->coded_union.data_offset;
-                break;
-            case fidl::kFidlTypeUnionPointer:
-                state = kStateUnionPointer;
-                union_pointer_state.union_type = fidl_type->coded_union_pointer.union_type;
-                break;
-            case fidl::kFidlTypeXUnion:
-                state = kStateXUnion;
-                xunion_state.fields = fidl_type->coded_xunion.fields;
-                xunion_state.field_count = fidl_type->coded_xunion.field_count;
-                break;
-            case fidl::kFidlTypeXUnionPointer:
-                state = kStateXUnionPointer;
-                xunion_pointer_state.xunion_type = fidl_type->coded_xunion_pointer.xunion_type;
-                break;
-            case fidl::kFidlTypeArray:
-                state = kStateArray;
-                array_state.element = fidl_type->coded_array.element;
-                array_state.array_size = fidl_type->coded_array.array_size;
-                array_state.element_size = fidl_type->coded_array.element_size;
-                break;
-            case fidl::kFidlTypeString:
-                state = kStateString;
-                string_state.max_size = fidl_type->coded_string.max_size;
-                string_state.nullable = fidl_type->coded_string.nullable;
-                break;
-            case fidl::kFidlTypeHandle:
-                state = kStateHandle;
-                handle_state.nullable = fidl_type->coded_handle.nullable;
-                break;
-            case fidl::kFidlTypeVector:
-                state = kStateVector;
-                vector_state.element = fidl_type->coded_vector.element;
-                vector_state.max_count = fidl_type->coded_vector.max_count;
-                vector_state.element_size = fidl_type->coded_vector.element_size;
-                vector_state.nullable = fidl_type->coded_vector.nullable;
-                break;
-            }
-        }
-
-        Frame(const fidl::FidlCodedStruct* coded_struct, uint32_t offset)
-            : offset(offset) {
-            state = kStateStruct;
-            struct_state.fields = coded_struct->fields;
-            struct_state.field_count = coded_struct->field_count;
-        }
-
-        Frame(const fidl::FidlCodedTable* coded_table, uint32_t offset)
-            : offset(offset) {
-            state = kStateStruct;
-            table_state.fields = coded_table->fields;
-            table_state.field_count = coded_table->field_count;
-        }
-
-        Frame(const fidl::FidlCodedUnion* coded_union, uint32_t offset)
-            : offset(offset) {
-            state = kStateUnion;
-            union_state.types = coded_union->types;
-            union_state.type_count = coded_union->type_count;
-            union_state.data_offset = coded_union->data_offset;
-        }
-
-        Frame(const fidl::FidlCodedXUnion* coded_xunion, uint32_t offset)
-            : state(kStateXUnion), offset(offset) {
-            // This initialization is done in the ctor body instead of in an
-            // initialization list since we need to set fields in unions, which
-            // is much more involved in a ctor initialization list.
-            xunion_state.fields = coded_xunion->fields;
-            xunion_state.field_count = coded_xunion->field_count;
-        }
-
-        Frame(const fidl_type_t* element, uint32_t array_size, uint32_t element_size,
-              uint32_t offset)
-            : offset(offset) {
-            state = kStateArray;
-            array_state.element = element;
-            array_state.array_size = array_size;
-            array_state.element_size = element_size;
-        }
-
-        // The default constructor does nothing when initializing the stack of frames.
-        Frame() {}
-
-        static Frame DoneSentinel() {
-            Frame frame;
-            frame.state = kStateDone;
-            return frame;
-        }
-
-        uint32_t NextStructField() {
-            ZX_DEBUG_ASSERT(state == kStateStruct);
-
-            uint32_t current = field;
-            field += 1;
-            return current;
-        }
-
-        uint32_t NextArrayOffset() {
-            ZX_DEBUG_ASSERT(state == kStateArray);
-
-            uint32_t current = field;
-            field += array_state.element_size;
-            return current;
-        }
-
-        enum : int {
-            kStateStruct,
-            kStateStructPointer,
-            kStateTable,
-            kStateTablePointer,
-            kStateUnion,
-            kStateUnionPointer,
-            kStateArray,
-            kStateString,
-            kStateHandle,
-            kStateVector,
-            kStateXUnion,
-            kStateXUnionPointer,
-
-            kStateDone,
-        } state;
-        // A byte offset into bytes_;
-        uint32_t offset;
-
-        // This is a subset of the information recorded in the
-        // fidl_type structures needed for decoding state. For
-        // example, struct sizes do not need to be present here.
-        union {
-            struct {
-                const fidl::FidlStructField* fields;
-                uint32_t field_count;
-            } struct_state;
-            struct {
-                const fidl::FidlCodedStruct* struct_type;
-            } struct_pointer_state;
-            struct {
-                const fidl::FidlTableField* fields;
-                uint32_t known_index;
-                uint32_t field_count;
-                uint32_t present_count;
-                uint32_t end_offset;
-                uint32_t end_handle;
-            } table_state;
-            struct {
-                const fidl::FidlCodedTable* table_type;
-            } table_pointer_state;
-            struct {
-                const fidl_type_t* const* types;
-                uint32_t type_count;
-                uint32_t data_offset;
-            } union_state;
-            struct {
-                const fidl::FidlCodedUnion* union_type;
-            } union_pointer_state;
-            struct {
-                const fidl::FidlXUnionField* fields;
-                uint32_t field_count;
-            } xunion_state;
-            struct {
-                const fidl::FidlCodedXUnion* xunion_type;
-            } xunion_pointer_state;
-            struct {
-                const fidl_type_t* element;
-                uint32_t array_size;
-                uint32_t element_size;
-            } array_state;
-            struct {
-                uint32_t max_size;
-                bool nullable;
-            } string_state;
-            struct {
-                bool nullable;
-            } handle_state;
-            struct {
-                const fidl_type* element;
-                uint32_t max_count;
-                uint32_t element_size;
-                bool nullable;
-            } vector_state;
-        };
-
-        uint32_t field = 0u;
-    };
-
-    // Returns true on success and false on recursion overflow.
-    bool Push(Frame frame) {
-        if (depth_ == FIDL_RECURSION_DEPTH) {
-            return false;
-        }
-        decoding_frames_[depth_] = frame;
-        ++depth_;
-        return true;
-    }
-
-    void Pop() {
-        ZX_DEBUG_ASSERT(depth_ != 0u);
-        --depth_;
-    }
-
-    Frame* Peek() {
-        ZX_DEBUG_ASSERT(depth_ != 0u);
-        return &decoding_frames_[depth_ - 1];
-    }
-
-    // Message state passed in to the constructor.
-    const fidl_type_t* const type_;
-
-    // Internal state.
-    uint32_t handle_idx_ = 0u;
-    uint32_t out_of_line_offset_ = 0u;
-
-    // Decoding stack state.
-    uint32_t depth_ = 0u;
-    Frame decoding_frames_[FIDL_RECURSION_DEPTH];
-};
-
-} // namespace internal
-} // namespace fidl
-
-#endif // ZIRCON_SYSTEM_ULIB_FIDL_BUFFER_WALKER_H_
diff --git a/system/ulib/fidl/decoding.cpp b/system/ulib/fidl/decoding.cpp
index 732e779..656280e 100644
--- a/system/ulib/fidl/decoding.cpp
+++ b/system/ulib/fidl/decoding.cpp
@@ -16,6 +16,7 @@
 #include <zircon/syscalls.h>
 #endif
 
+#include "envelope_frames.h"
 #include "visitor.h"
 #include "walker.h"
 
@@ -52,6 +53,8 @@
 constexpr uintptr_t kAllocPresenceMarker = FIDL_ALLOC_PRESENT;
 constexpr uintptr_t kAllocAbsenceMarker = FIDL_ALLOC_ABSENT;
 
+using EnvelopeState = ::fidl::EnvelopeFrames::EnvelopeState;
+
 class FidlDecoder final : public fidl::Visitor<
     fidl::MutatingVisitorTrait, StartingPoint, Position> {
 public:
@@ -75,19 +78,11 @@
             SetError("decoder encountered invalid pointer");
             return Status::kConstraintViolationError;
         }
-        // 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 new_offset = next_out_of_line_;
-        if (add_overflow(new_offset, inline_size, &new_offset)
-            || add_overflow(new_offset, mask, &new_offset)) {
+        uint32_t new_offset;
+        if (!fidl::AddOutOfLine(next_out_of_line_, inline_size, &new_offset)) {
             SetError("overflow updating out-of-line offset");
             return Status::kMemoryError;
         }
-        new_offset &= ~mask;
-
         if (new_offset > num_bytes_) {
             SetError("message tried to decode more than provided number of bytes");
             return Status::kMemoryError;
@@ -143,7 +138,7 @@
         }
         // Remember the current watermark of bytes and handles, so that after processing
         // the envelope, we can validate that the claimed num_bytes/num_handles matches the reality.
-        if (!Push(next_out_of_line_, handle_idx_)) {
+        if (!envelope_frames_.Push(EnvelopeState(next_out_of_line_, handle_idx_))) {
             SetError("Overly deep nested envelopes");
             return Status::kConstraintViolationError;
         }
@@ -161,7 +156,7 @@
 
     Status LeaveEnvelope(Position envelope_position, EnvelopePointer envelope) {
         // Now that the envelope has been consumed, check the correctness of the envelope header.
-        auto& starting_state = Pop();
+        auto& starting_state = envelope_frames_.Pop();
         uint32_t num_bytes = next_out_of_line_ - starting_state.bytes_so_far;
         uint32_t num_handles = handle_idx_ - starting_state.handles_so_far;
         if (envelope->num_bytes != num_bytes) {
@@ -197,29 +192,6 @@
         *out_error_msg_ = error;
     }
 
-    struct EnvelopeState {
-        uint32_t bytes_so_far;
-        uint32_t handles_so_far;
-    };
-
-    const EnvelopeState& Pop() {
-        ZX_ASSERT(envelope_depth_ != 0);
-        envelope_depth_--;
-        return envelope_states_[envelope_depth_];
-    }
-
-    bool Push(uint32_t num_bytes, uint32_t num_handles) {
-        if (envelope_depth_ == FIDL_RECURSION_DEPTH) {
-            return false;
-        }
-        envelope_states_[envelope_depth_] = (EnvelopeState) {
-            .bytes_so_far = num_bytes,
-            .handles_so_far = num_handles,
-        };
-        envelope_depth_++;
-        return true;
-    }
-
     // Message state passed in to the constructor.
     uint8_t* const bytes_;
     const uint32_t num_bytes_;
@@ -231,8 +203,7 @@
     // Decoder state
     zx_status_t status_ = ZX_OK;
     uint32_t handle_idx_ = 0;
-    uint32_t envelope_depth_ = 0;
-    EnvelopeState envelope_states_[FIDL_RECURSION_DEPTH];
+    fidl::EnvelopeFrames envelope_frames_;
 };
 
 } // namespace
@@ -251,28 +222,17 @@
         set_error("Cannot provide non-zero handle count and null handle pointer");
         return ZX_ERR_INVALID_ARGS;
     }
-    if (type == nullptr) {
-        set_error("Cannot decode a null fidl type");
-        return ZX_ERR_INVALID_ARGS;
-    }
 
-    size_t primary_size;
+    uint32_t next_out_of_line;
     zx_status_t status;
-    if ((status = fidl::GetPrimaryObjectSize(type, &primary_size, out_error_msg)) != ZX_OK) {
+    if ((status = fidl::StartingOutOfLineOffset(type,
+                                                num_bytes,
+                                                &next_out_of_line,
+                                                out_error_msg)) != ZX_OK) {
         return status;
     }
-    if (primary_size > num_bytes) {
-        set_error("Buffer is too small for first inline object");
-        return ZX_ERR_INVALID_ARGS;
-    }
-    uint64_t next_out_of_line = fidl::FidlAlign(static_cast<uint32_t>(primary_size));
-    if (next_out_of_line > std::numeric_limits<uint32_t>::max()) {
-        set_error("Out of line starting offset overflows");
-        return ZX_ERR_INVALID_ARGS;
-    }
 
-    FidlDecoder decoder(bytes, num_bytes, handles, num_handles,
-                        static_cast<uint32_t>(next_out_of_line), out_error_msg);
+    FidlDecoder decoder(bytes, num_bytes, handles, num_handles, next_out_of_line, out_error_msg);
     fidl::Walk(decoder,
                type,
                StartingPoint { reinterpret_cast<uint8_t*>(bytes) });
diff --git a/system/ulib/fidl/encoding.cpp b/system/ulib/fidl/encoding.cpp
index f94a65c..81a5ee2 100644
--- a/system/ulib/fidl/encoding.cpp
+++ b/system/ulib/fidl/encoding.cpp
@@ -17,6 +17,7 @@
 #include <zircon/syscalls.h>
 #endif
 
+#include "envelope_frames.h"
 #include "visitor.h"
 #include "walker.h"
 
@@ -50,6 +51,8 @@
     return Position { 0 };
 }
 
+using EnvelopeState = ::fidl::EnvelopeFrames::EnvelopeState;
+
 class FidlEncoder final : public fidl::Visitor<
     fidl::MutatingVisitorTrait, StartingPoint, Position> {
 public:
@@ -93,7 +96,7 @@
         }
         handles_[handle_idx_] = *handle;
         *handle = FIDL_HANDLE_PRESENT;
-        handle_idx_ += 1;
+        handle_idx_++;
         return Status::kSuccess;
     }
 
@@ -101,11 +104,10 @@
                          EnvelopePointer envelope,
                          const fidl_type_t* payload_type) {
         // Validate envelope data/bytes invariants
-        if (envelope->data == nullptr) {
-            if (!(envelope->num_bytes == 0 && envelope->num_handles == 0)) {
-                SetError("Envelope has absent data pointer, yet has data and/or handles");
-                return Status::kConstraintViolationError;
-            }
+        if (envelope->data == nullptr &&
+            (envelope->num_bytes != 0 || envelope->num_handles != 0)) {
+            SetError("Envelope has absent data pointer, yet has data and/or handles");
+            return Status::kConstraintViolationError;
         }
         if (envelope->data != nullptr && envelope->num_bytes == 0) {
             SetError("Envelope has present data pointer, but zero byte count");
@@ -119,7 +121,7 @@
         }
         // Remember the current watermark of bytes and handles, so that after processing
         // the envelope, we can validate that the claimed num_bytes/num_handles matches the reality.
-        if (!Push(next_out_of_line_, handle_idx_)) {
+        if (!envelope_frames_.Push(EnvelopeState(next_out_of_line_, handle_idx_))) {
             SetError("Overly deep nested envelopes");
             return Status::kConstraintViolationError;
         }
@@ -128,7 +130,7 @@
 
     Status LeaveEnvelope(Position envelope_position, EnvelopePointer envelope) {
         // Now that the envelope has been consumed, check the correctness of the envelope header.
-        auto& starting_state = Pop();
+        auto& starting_state = envelope_frames_.Pop();
         uint32_t num_bytes = next_out_of_line_ - starting_state.bytes_so_far;
         uint32_t num_handles = handle_idx_ - starting_state.handles_so_far;
         if (envelope->num_bytes != num_bytes) {
@@ -174,19 +176,11 @@
             SetError("noncontiguous out of line storage during encode");
             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 new_offset = next_out_of_line_;
-        if (add_overflow(new_offset, size, &new_offset)
-            || add_overflow(new_offset, mask, &new_offset)) {
+        uint32_t new_offset;
+        if (!fidl::AddOutOfLine(next_out_of_line_, size, &new_offset)) {
             SetError("overflow updating out-of-line offset");
             return false;
         }
-        new_offset &= ~mask;
-
         if (new_offset > num_bytes_) {
             SetError("message tried to encode more than provided number of bytes");
             return false;
@@ -196,29 +190,6 @@
         return true;
     }
 
-    struct EnvelopeState {
-        uint32_t bytes_so_far;
-        uint32_t handles_so_far;
-    };
-
-    const EnvelopeState& Pop() {
-        ZX_ASSERT(envelope_depth_ != 0);
-        envelope_depth_ -= 1;
-        return envelope_states_[envelope_depth_];
-    }
-
-    bool Push(uint32_t num_bytes, uint32_t num_handles) {
-        if (envelope_depth_ == FIDL_RECURSION_DEPTH) {
-            return false;
-        }
-        envelope_states_[envelope_depth_] = (EnvelopeState) {
-            .bytes_so_far = num_bytes,
-            .handles_so_far = num_handles,
-        };
-        envelope_depth_ += 1;
-        return true;
-    }
-
     // Message state passed in to the constructor.
     uint8_t* const bytes_;
     const uint32_t num_bytes_;
@@ -230,8 +201,7 @@
     // Encoder state
     zx_status_t status_ = ZX_OK;
     uint32_t handle_idx_ = 0;
-    uint32_t envelope_depth_ = 0;
-    EnvelopeState envelope_states_[FIDL_RECURSION_DEPTH];
+    fidl::EnvelopeFrames envelope_frames_;
 };
 
 } // namespace
@@ -254,28 +224,17 @@
         set_error("Cannot encode with null out_actual_handles");
         return ZX_ERR_INVALID_ARGS;
     }
-    if (type == nullptr) {
-        set_error("Cannot encode a null fidl type");
-        return ZX_ERR_INVALID_ARGS;
-    }
 
-    size_t primary_size;
+    uint32_t next_out_of_line;
     zx_status_t status;
-    if ((status = fidl::GetPrimaryObjectSize(type, &primary_size, out_error_msg)) != ZX_OK) {
+    if ((status = fidl::StartingOutOfLineOffset(type,
+                                                num_bytes,
+                                                &next_out_of_line,
+                                                out_error_msg)) != ZX_OK) {
         return status;
     }
-    if (primary_size > num_bytes) {
-        set_error("Buffer is too small for first inline object");
-        return ZX_ERR_INVALID_ARGS;
-    }
-    uint64_t next_out_of_line = fidl::FidlAlign(static_cast<uint32_t>(primary_size));
-    if (next_out_of_line > std::numeric_limits<uint32_t>::max()) {
-        set_error("Out of line starting offset overflows");
-        return ZX_ERR_INVALID_ARGS;
-    }
 
-    FidlEncoder encoder(bytes, num_bytes, handles, max_handles,
-                        static_cast<uint32_t>(next_out_of_line), out_error_msg);
+    FidlEncoder encoder(bytes, num_bytes, handles, max_handles, next_out_of_line, out_error_msg);
     fidl::Walk(encoder,
                type,
                StartingPoint { reinterpret_cast<uint8_t*>(bytes) });
diff --git a/system/ulib/fidl/envelope_frames.h b/system/ulib/fidl/envelope_frames.h
new file mode 100644
index 0000000..5dbb27d
--- /dev/null
+++ b/system/ulib/fidl/envelope_frames.h
@@ -0,0 +1,53 @@
+// Copyright 2019 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
+
+#include <stdalign.h>
+#include <cstdint>
+#include <cstdlib>
+
+#include <lib/fidl/coding.h>
+#include <zircon/assert.h>
+#include <zircon/compiler.h>
+
+namespace fidl {
+
+class EnvelopeFrames {
+public:
+    struct EnvelopeState {
+        uint32_t bytes_so_far;
+        uint32_t handles_so_far;
+
+        EnvelopeState(uint32_t bytes_so_far, uint32_t handles_so_far)
+            : bytes_so_far(bytes_so_far), handles_so_far(handles_so_far) {}
+
+    private:
+        // Default constructor used by |EnvelopeFrames| to avoid unnecessarily zeroing
+        // the |envelope_states_| array.
+        EnvelopeState() = default;
+        friend class EnvelopeFrames;
+    };
+
+    const EnvelopeState& Pop() {
+        ZX_ASSERT(envelope_depth_ != 0);
+        envelope_depth_--;
+        return envelope_states_[envelope_depth_];
+    }
+
+    bool Push(const EnvelopeState& state) {
+        if (envelope_depth_ == FIDL_RECURSION_DEPTH) {
+            return false;
+        }
+        envelope_states_[envelope_depth_] = state;
+        envelope_depth_++;
+        return true;
+    }
+
+private:
+    uint32_t envelope_depth_ = 0;
+    EnvelopeState envelope_states_[FIDL_RECURSION_DEPTH];
+};
+
+} // namespace fidl
diff --git a/system/ulib/fidl/include/lib/fidl/coding.h b/system/ulib/fidl/include/lib/fidl/coding.h
index f3add07..3768597 100644
--- a/system/ulib/fidl/include/lib/fidl/coding.h
+++ b/system/ulib/fidl/include/lib/fidl/coding.h
@@ -38,7 +38,7 @@
 zx_status_t fidl_validate_msg(const fidl_type_t* type, const fidl_msg_t* msg,
                               const char** out_error_msg);
 
-// Traverses a FIDL message starting at |value|, closing all handles within it.
+// Traverses a decoded FIDL message starting at |value|, closing all handles within it.
 // If the message is non-contiguous in memory, the function will follow pointers and close handles
 // in any scattered out-of-line objects.
 //
diff --git a/system/ulib/fidl/include/lib/fidl/internal.h b/system/ulib/fidl/include/lib/fidl/internal.h
index b814fd9..036757c 100644
--- a/system/ulib/fidl/include/lib/fidl/internal.h
+++ b/system/ulib/fidl/include/lib/fidl/internal.h
@@ -30,11 +30,27 @@
     kNullable = 1u,
 };
 
-inline uint64_t FidlAlign(uint32_t offset) {
+constexpr inline uint64_t FidlAlign(uint32_t offset) {
     constexpr uint64_t alignment_mask = FIDL_ALIGNMENT - 1;
     return (offset + alignment_mask) & ~alignment_mask;
 }
 
+// Add |size| to out-of-line |offset|, maintaining alignment. 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.
+// Returns false on overflow. Otherwise, resulting offset is stored in |out_offset|.
+inline bool AddOutOfLine(uint32_t offset, uint32_t size, uint32_t* out_offset) {
+    constexpr uint32_t kMask = FIDL_ALIGNMENT - 1;
+    uint32_t new_offset = offset;
+    if (add_overflow(new_offset, size, &new_offset)
+        || add_overflow(new_offset, kMask, &new_offset)) {
+        return false;
+    }
+    new_offset &= ~kMask;
+    *out_offset = new_offset;
+    return true;
+}
+
 struct FidlStructField {
     const fidl_type* type;
     uint32_t offset;
diff --git a/system/ulib/fidl/linearizing.cpp b/system/ulib/fidl/linearizing.cpp
index 9e1f087..3713745 100644
--- a/system/ulib/fidl/linearizing.cpp
+++ b/system/ulib/fidl/linearizing.cpp
@@ -14,6 +14,7 @@
 #include <zircon/assert.h>
 #include <zircon/compiler.h>
 
+#include "envelope_frames.h"
 #include "visitor.h"
 #include "walker.h"
 
@@ -63,6 +64,8 @@
     };
 }
 
+using EnvelopeState = ::fidl::EnvelopeFrames::EnvelopeState;
+
 class FidlLinearizer final : public fidl::Visitor<
     fidl::MutatingVisitorTrait, StartingPoint, Position> {
 public:
@@ -83,18 +86,11 @@
                         ObjectPointerPointer object_ptr_ptr,
                         uint32_t inline_size,
                         Position* out_position) {
-        // 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 new_offset = next_out_of_line_;
-        if (add_overflow(new_offset, inline_size, &new_offset) ||
-            add_overflow(new_offset, mask, &new_offset)) {
+        uint32_t new_offset;
+        if (!fidl::AddOutOfLine(next_out_of_line_, inline_size, &new_offset)) {
             SetError("out-of-line offset overflow trying to linearize");
             return Status::kMemoryError;
         }
-        new_offset &= ~mask;
 
         if (new_offset > num_bytes_) {
             SetError("object is too big to linearize into provided buffer",
@@ -144,7 +140,7 @@
         }
         // Remember the current watermark of bytes and handles, so that after processing
         // the envelope, we can validate that the claimed num_bytes/num_handles matches the reality.
-        if (!Push(next_out_of_line_, handle_idx_)) {
+        if (!envelope_frames_.Push(EnvelopeState(next_out_of_line_, handle_idx_))) {
             SetError("Overly deep nested envelopes");
             return Status::kConstraintViolationError;
         }
@@ -154,7 +150,7 @@
     Status LeaveEnvelope(Position envelope_position, EnvelopePointer envelope) {
         // Now that the envelope has been consumed, go back and update the envelope header with
         // the correct num_bytes and num_handles values
-        auto& starting_state = Pop();
+        auto& starting_state = envelope_frames_.Pop();
         uint32_t num_bytes = next_out_of_line_ - starting_state.bytes_so_far;
         uint32_t num_handles = handle_idx_ - starting_state.handles_so_far;
         envelope->num_bytes = num_bytes;
@@ -187,29 +183,6 @@
         }
     }
 
-    struct EnvelopeState {
-        uint32_t bytes_so_far;
-        uint32_t handles_so_far;
-    };
-
-    const EnvelopeState& Pop() {
-        ZX_ASSERT(envelope_depth_ != 0);
-        envelope_depth_ -= 1;
-        return envelope_states_[envelope_depth_];
-    }
-
-    bool Push(uint32_t num_bytes, uint32_t num_handles) {
-        if (envelope_depth_ == FIDL_RECURSION_DEPTH) {
-            return false;
-        }
-        envelope_states_[envelope_depth_] = (EnvelopeState) {
-            .bytes_so_far = num_bytes,
-            .handles_so_far = num_handles,
-        };
-        envelope_depth_ += 1;
-        return true;
-    }
-
     // Message state passed into the constructor.
     uint8_t* const bytes_;
     const uint32_t num_bytes_;
@@ -220,8 +193,7 @@
     zx_status_t status_ = ZX_OK;
     uint32_t handle_idx_ = 0;
     zx_handle_t* original_handles_[ZX_CHANNEL_MAX_MSG_HANDLES];
-    uint32_t envelope_depth_ = 0;
-    EnvelopeState envelope_states_[FIDL_RECURSION_DEPTH];
+    fidl::EnvelopeFrames envelope_frames_;
 };
 
 } // namespace
@@ -240,14 +212,10 @@
         set_error("Cannot linearize with null destination buffer");
         return ZX_ERR_INVALID_ARGS;
     }
-    if (type == nullptr) {
-        set_error("Cannot linearize a null fidl type");
-        return ZX_ERR_INVALID_ARGS;
-    }
 
     size_t primary_size;
     zx_status_t status;
-    if ((status = fidl::GetPrimaryObjectSize(type, &primary_size, out_error_msg)) != ZX_OK) {
+    if ((status = fidl::PrimaryObjectSize(type, &primary_size, out_error_msg)) != ZX_OK) {
         return status;
     }
     if (primary_size > num_bytes) {
diff --git a/system/ulib/fidl/validating.cpp b/system/ulib/fidl/validating.cpp
index 342face..e3ca4a8 100644
--- a/system/ulib/fidl/validating.cpp
+++ b/system/ulib/fidl/validating.cpp
@@ -12,74 +12,211 @@
 #include <zircon/assert.h>
 #include <zircon/compiler.h>
 
-#include "buffer_walker.h"
+#include "envelope_frames.h"
+#include "visitor.h"
+#include "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;
+struct Position;
 
+struct StartingPoint {
+    const uint8_t* const addr;
+    Position ToPosition() const;
+};
+
+struct Position {
+    uint32_t offset;
+    Position operator+(uint32_t size) const {
+        return Position { offset + size };
+    }
+    Position& operator+=(uint32_t size) {
+        offset += size;
+        return *this;
+    }
+    template <typename T>
+    constexpr const T* Get(StartingPoint start) const {
+        return reinterpret_cast<const T*>(start.addr + offset);
+    }
+};
+
+Position StartingPoint::ToPosition() const {
+    return Position { 0 };
+}
+
+constexpr uintptr_t kAllocPresenceMarker = FIDL_ALLOC_PRESENT;
+constexpr uintptr_t kAllocAbsenceMarker = FIDL_ALLOC_ABSENT;
+
+using EnvelopeState = ::fidl::EnvelopeFrames::EnvelopeState;
+
+class FidlValidator final : public fidl::Visitor<
+    fidl::NonMutatingVisitorTrait, StartingPoint, Position> {
 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) {}
+    FidlValidator(uint32_t num_bytes, uint32_t num_handles, uint32_t next_out_of_line,
+                  const char** out_error_msg)
+        : num_bytes_(num_bytes), num_handles_(num_handles), next_out_of_line_(next_out_of_line),
+          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;
+    using StartingPoint = StartingPoint;
+
+    using Position = Position;
+
+    static constexpr bool kContinueAfterConstraintViolation = true;
+
+    Status VisitPointer(Position ptr_position,
+                        ObjectPointerPointer object_ptr_ptr,
+                        uint32_t inline_size,
+                        Position* out_position) {
+        if (reinterpret_cast<uintptr_t>(*object_ptr_ptr) != kAllocPresenceMarker) {
+            SetError("validator encountered invalid pointer");
+            return Status::kConstraintViolationError;
         }
-    }
-
-    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;
+        uint32_t new_offset;
+        if (!fidl::AddOutOfLine(next_out_of_line_, inline_size, &new_offset)) {
+            SetError("overflow updating out-of-line offset");
+            return Status::kMemoryError;
         }
+        if (new_offset > num_bytes_) {
+            SetError("message tried to access more than provided number of bytes");
+            return Status::kMemoryError;
+        }
+        *out_position = Position { next_out_of_line_ };
+        next_out_of_line_ = new_offset;
+        return Status::kSuccess;
+    }
+
+    Status VisitHandle(Position handle_position, HandlePointer handle) {
+        if (*handle != FIDL_HANDLE_PRESENT) {
+            SetError("message contains a garbage handle");
+            return Status::kConstraintViolationError;
+        }
+        if (handle_idx_ == num_handles_) {
+            SetError("message has too many handles");
+            return Status::kConstraintViolationError;
+        }
+        handle_idx_++;
+        return Status::kSuccess;
+    }
+
+    Status EnterEnvelope(Position envelope_position,
+                         EnvelopePointer envelope,
+                         const fidl_type_t* payload_type) {
+        if (envelope->presence == kAllocAbsenceMarker &&
+            (envelope->num_bytes != 0 || envelope->num_handles != 0)) {
+            SetError("Envelope has absent data pointer, yet has data and/or handles");
+            return Status::kConstraintViolationError;
+        }
+        if (envelope->presence != kAllocAbsenceMarker && envelope->num_bytes == 0) {
+            SetError("Envelope has present data pointer, but zero byte count");
+            return Status::kConstraintViolationError;
+        }
+        uint32_t expected_handle_count;
+        if (add_overflow(handle_idx_, envelope->num_handles, &expected_handle_count) ||
+            expected_handle_count > num_handles_) {
+            SetError("Envelope has more handles than expected");
+            return Status::kConstraintViolationError;
+        }
+        // Remember the current watermark of bytes and handles, so that after processing
+        // the envelope, we can validate that the claimed num_bytes/num_handles matches the reality.
+        if (!envelope_frames_.Push(EnvelopeState(next_out_of_line_, handle_idx_))) {
+            SetError("Overly deep nested envelopes");
+            return Status::kConstraintViolationError;
+        }
+        // If we do not have the coding table for this payload,
+        // treat it as unknown and add its contained handles
+        if (envelope->presence != kAllocAbsenceMarker && payload_type == nullptr) {
+            handle_idx_ += envelope->num_handles;
+        }
+        return Status::kSuccess;
+    }
+
+    Status LeaveEnvelope(Position envelope_position, EnvelopePointer envelope) {
+        // Now that the envelope has been consumed, check the correctness of the envelope header.
+        auto& starting_state = envelope_frames_.Pop();
+        uint32_t num_bytes = next_out_of_line_ - starting_state.bytes_so_far;
+        uint32_t num_handles = handle_idx_ - starting_state.handles_so_far;
+        if (envelope->num_bytes != num_bytes) {
+            SetError("Envelope num_bytes was mis-sized");
+            return Status::kConstraintViolationError;
+        }
+        if (envelope->num_handles != num_handles) {
+            SetError("Envelope num_handles was mis-sized");
+            return Status::kConstraintViolationError;
+        }
+        return Status::kSuccess;
+    }
+
+    void OnError(const char* error) {
+        SetError(error);
     }
 
     zx_status_t status() const { return status_; }
 
+    bool DidConsumeAllBytes() const { return next_out_of_line_ == num_bytes_; }
+
+    bool DidConsumeAllHandles() const { return handle_idx_ == num_handles_; }
+
 private:
+    void SetError(const char* error) {
+        if (status_ == ZX_OK) {
+            status_ = ZX_ERR_INVALID_ARGS;
+            if (out_error_msg_ != nullptr) {
+                *out_error_msg_ = error;
+            }
+        }
+    }
+
     // Message state passed in to the constructor.
-    const uint8_t* const bytes_;
     const uint32_t num_bytes_;
     const uint32_t num_handles_;
+    uint32_t next_out_of_line_;
     const char** const out_error_msg_;
+
+    // Validator state
     zx_status_t status_ = ZX_OK;
+    uint32_t handle_idx_ = 0;
+    fidl::EnvelopeFrames envelope_frames_;
 };
 
 } // 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();
+    auto set_error = [&out_error_msg] (const char* msg) {
+        if (out_error_msg) *out_error_msg = msg;
+    };
+    if (bytes == nullptr) {
+        set_error("Cannot validate null bytes");
+        return ZX_ERR_INVALID_ARGS;
+    }
+
+    uint32_t next_out_of_line;
+    zx_status_t status;
+    if ((status = fidl::StartingOutOfLineOffset(type,
+                                                num_bytes,
+                                                &next_out_of_line,
+                                                out_error_msg)) != ZX_OK) {
+        return status;
+    }
+
+    FidlValidator validator(num_bytes, num_handles, next_out_of_line, out_error_msg);
+    fidl::Walk(validator,
+               type,
+               StartingPoint { reinterpret_cast<const uint8_t*>(bytes) });
+
+    if (validator.status() == ZX_OK) {
+        if (!validator.DidConsumeAllBytes()) {
+            set_error("message did not consume all provided bytes");
+            return ZX_ERR_INVALID_ARGS;
+        }
+        if (!validator.DidConsumeAllHandles()) {
+            set_error("message did not reference all provided handles");
+            return ZX_ERR_INVALID_ARGS;
+        }
+    }
+
     return validator.status();
 }
 
diff --git a/system/ulib/fidl/visitor.h b/system/ulib/fidl/visitor.h
index ea3f46a..fea0cf6 100644
--- a/system/ulib/fidl/visitor.h
+++ b/system/ulib/fidl/visitor.h
@@ -23,7 +23,7 @@
     static constexpr bool kIsConst = true;
 
     // Message is const
-    using ObjectPointerPointer = const void** const;
+    using ObjectPointerPointer = const void* const* const;
 };
 
 struct MutatingVisitorTrait {
@@ -31,7 +31,7 @@
     static constexpr bool kIsConst = false;
 
     // Message is mutable
-    using ObjectPointerPointer = void**;
+    using ObjectPointerPointer = void** const;
 };
 
 namespace {
@@ -59,7 +59,8 @@
     using MutationTrait = MutationTrait_;
 
     template <typename T>
-    using Ptr = typename std::conditional<MutationTrait::kIsConst, std::add_const<T*>, T*>::type;
+    using Ptr = typename std::conditional<MutationTrait::kIsConst,
+                                          typename std::add_const<T>::type, T>::type*;
 
     // A type encapsulating the starting point of message traversal.
     //
diff --git a/system/ulib/fidl/walker.cpp b/system/ulib/fidl/walker.cpp
index f06e491..5039ef0 100644
--- a/system/ulib/fidl/walker.cpp
+++ b/system/ulib/fidl/walker.cpp
@@ -2,13 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <cstdint>
+#include <limits>
+
 #include "walker.h"
 
 namespace fidl {
 
-zx_status_t GetPrimaryObjectSize(const fidl_type_t* type,
-                                 size_t* out_size,
-                                 const char** out_error) {
+zx_status_t PrimaryObjectSize(const fidl_type_t* type,
+                              size_t* out_size,
+                              const char** out_error) {
+    auto set_error = [&out_error] (const char* msg) {
+        if (out_error) *out_error = msg;
+    };
+    if (type == nullptr) {
+        set_error("fidl type cannot be null");
+        return ZX_ERR_INVALID_ARGS;
+    }
     switch (type->type_tag) {
         case fidl::kFidlTypeStruct:
             *out_size = type->coded_struct.size;
@@ -17,11 +27,34 @@
             *out_size = sizeof(fidl_vector_t);
             return ZX_OK;
         default:
-            if (out_error) {
-                *out_error = "Message must be a struct or a table";
-            }
+            set_error("Message must be a struct or a table");
             return ZX_ERR_INVALID_ARGS;
     }
 }
 
+zx_status_t StartingOutOfLineOffset(const fidl_type_t* type,
+                                    uint32_t buffer_size,
+                                    uint32_t* out_first_out_of_line,
+                                    const char** out_error) {
+    auto set_error = [&out_error] (const char* msg) {
+        if (out_error) *out_error = msg;
+    };
+    size_t primary_size;
+    zx_status_t status;
+    if ((status = PrimaryObjectSize(type, &primary_size, out_error)) != ZX_OK) {
+        return status;
+    }
+    if (primary_size > buffer_size) {
+        set_error("Buffer is too small for first inline object");
+        return ZX_ERR_INVALID_ARGS;
+    }
+    uint64_t first_out_of_line = fidl::FidlAlign(static_cast<uint32_t>(primary_size));
+    if (first_out_of_line > std::numeric_limits<uint32_t>::max()) {
+        set_error("Out of line starting offset overflows");
+        return ZX_ERR_INVALID_ARGS;
+    }
+    *out_first_out_of_line = static_cast<uint32_t>(first_out_of_line);
+    return ZX_OK;
+}
+
 }
diff --git a/system/ulib/fidl/walker.h b/system/ulib/fidl/walker.h
index 1b1cec0..bc1cab2 100644
--- a/system/ulib/fidl/walker.h
+++ b/system/ulib/fidl/walker.h
@@ -64,7 +64,7 @@
     __builtin_unreachable();
 }
 
-// The Walker class traverses through a FIDL message by following its encoding table and
+// The Walker class traverses through a FIDL message by following its coding table and
 // calling the visitor implementation. VisitorImpl must be a concrete implementation of the
 // fidl::Visitor interface. The concrete type is used to eliminate dynamic dispatch.
 template <typename VisitorImpl>
@@ -230,7 +230,7 @@
             ZX_DEBUG_ASSERT(state == kStateStruct);
 
             uint32_t current = struct_state.field;
-            struct_state.field += 1;
+            struct_state.field++;
             return current;
         }
 
@@ -263,7 +263,7 @@
         Position position;
 
         // This is a subset of the information recorded in the
-        // fidl_type structures needed for decoding state. For
+        // fidl_type structures needed for coding state. For
         // example, struct sizes do not need to be present here.
         union {
             struct {
@@ -348,7 +348,7 @@
         if (depth_ == FIDL_RECURSION_DEPTH) {
             return false;
         }
-        decoding_frames_[depth_] = frame;
+        coding_frames_[depth_] = frame;
         ++depth_;
         return true;
     }
@@ -360,7 +360,7 @@
 
     Frame* Peek() {
         ZX_DEBUG_ASSERT(depth_ != 0u);
-        return &decoding_frames_[depth_ - 1];
+        return &coding_frames_[depth_ - 1];
     }
 
     const fidl_type_t* const type_;
@@ -368,7 +368,7 @@
 
     // Decoding stack state.
     uint32_t depth_ = 0u;
-    Frame decoding_frames_[FIDL_RECURSION_DEPTH];
+    Frame coding_frames_[FIDL_RECURSION_DEPTH];
 };
 
 template <typename VisitorImpl>
@@ -416,12 +416,12 @@
                 continue;
             }
             case Frame::kStateStructPointer: {
-                if (*PtrTo<void*>(frame->position) == nullptr) {
+                if (*PtrTo<Ptr<void>>(frame->position) == nullptr) {
                     Pop();
                     continue;
                 }
                 auto status = visitor.VisitPointer(frame->position,
-                                                   PtrTo<void*>(frame->position),
+                                                   PtrTo<Ptr<void>>(frame->position),
                                                    frame->struct_pointer_state.struct_type->size,
                                                    &frame->position);
                 FIDL_STATUS_GUARD(status);
@@ -478,14 +478,14 @@
                     const fidl::FidlTableField* field = table_frame.field;
                     if (field->ordinal == table_frame.ordinal) {
                         known_field = field;
-                        table_frame.field += 1;
-                        table_frame.remaining_fields -= 1;
+                        table_frame.field++;
+                        table_frame.remaining_fields--;
                     }
                 }
                 Position envelope_pos = envelope_position(table_frame.ordinal);
                 auto envelope_ptr = PtrTo<fidl_envelope_t>(envelope_pos);
                 // Process the next ordinal in the following state machine iteration
-                table_frame.ordinal += 1;
+                table_frame.ordinal++;
                 // Make sure we don't process a malformed envelope
                 const fidl_type_t* payload_type = known_field ? known_field->type : nullptr;
                 auto status = visitor.EnterEnvelope(envelope_pos, envelope_ptr, payload_type);
@@ -523,12 +523,12 @@
                 continue;
             }
             case Frame::kStateTablePointer: {
-                if (*PtrTo<fidl_vector_t*>(frame->position) == nullptr) {
+                if (*PtrTo<Ptr<fidl_vector_t>>(frame->position) == nullptr) {
                     Pop();
                     continue;
                 }
                 auto status = visitor.VisitPointer(frame->position,
-                                                   PtrTo<void*>(frame->position),
+                                                   PtrTo<Ptr<void>>(frame->position),
                                                    static_cast<uint32_t>(sizeof(fidl_vector_t)),
                                                    &frame->position);
                 FIDL_STATUS_GUARD(status);
@@ -552,12 +552,12 @@
                 continue;
             }
             case Frame::kStateUnionPointer: {
-                if (*PtrTo<fidl_union_tag_t*>(frame->position) == nullptr) {
+                if (*PtrTo<Ptr<fidl_union_tag_t>>(frame->position) == nullptr) {
                     Pop();
                     continue;
                 }
                 auto status = visitor.VisitPointer(frame->position,
-                                                   PtrTo<void*>(frame->position),
+                                                   PtrTo<Ptr<void>>(frame->position),
                                                    frame->union_pointer_state.union_type->size,
                                                    &frame->position);
                 FIDL_STATUS_GUARD(status);
@@ -625,12 +625,12 @@
                 continue;
             }
             case Frame::kStateXUnionPointer: {
-                if (*PtrTo<fidl_xunion_t*>(frame->position) == nullptr) {
+                if (*PtrTo<Ptr<fidl_xunion_t>>(frame->position) == nullptr) {
                     Pop();
                     continue;
                 }
                 auto status = visitor.VisitPointer(frame->position,
-                                                   PtrTo<void*>(frame->position),
+                                                   PtrTo<Ptr<void>>(frame->position),
                                                    static_cast<uint32_t>(sizeof(fidl_xunion_t)),
                                                    &frame->position);
                 FIDL_STATUS_GUARD(status);
@@ -678,8 +678,9 @@
                 }
                 Position position;
                 auto status = visitor.VisitPointer(position,
-                                                   &const_cast<Ptr<void>&>(
-                                                       reinterpret_cast<void*&>(string_ptr->data)),
+                                                   &reinterpret_cast<Ptr<void>&>(
+                                                       const_cast<Ptr<char>&>(
+                                                           string_ptr->data)),
                                                    static_cast<uint32_t>(size),
                                                    &position);
                 FIDL_STATUS_GUARD(status);
@@ -730,7 +731,7 @@
                                                    &frame->position);
                 FIDL_STATUS_GUARD(status);
                 if (frame->vector_state.element) {
-                    // Continue by decoding the vector elements as an array.
+                    // Continue by visiting the vector elements as an array.
                     *frame = Frame(frame->vector_state.element, size,
                                    frame->vector_state.element_size, frame->position);
                 } else {
@@ -763,13 +764,27 @@
     walker.Walk(visitor);
 }
 
-// Given a FIDL coding table, first ensure that the primary object is of one of the expected types,
-// then return the size of the primary object.
+// Infer the size of the primary object, from the coding table in |type|.
+// Ensures that the primary object is of one of the expected types.
 //
-// Currently the primary object must be either a struct or a table. An error is returned if this is
-// not the case.
-zx_status_t GetPrimaryObjectSize(const fidl_type_t* type,
-                                 size_t* out_size,
-                                 const char** out_error);
+// An error is returned if:
+// - |type| is null
+// - The primary object is neither a struct nor a table.
+zx_status_t PrimaryObjectSize(const fidl_type_t* type,
+                              size_t* out_size,
+                              const char** out_error);
+
+// Calculate the offset of the first out-of-line object, from the coding table in |type|.
+// Ensures that the primary object is of one of the expected types, and the offset falls within the
+// |buffer_size| constraints.
+//
+// An error is returned if:
+// - |type| is null
+// - The primary object is neither a struct nor a table.
+// - The offset overflows, or is larger than |buffer_size|.
+zx_status_t StartingOutOfLineOffset(const fidl_type_t* type,
+                                    uint32_t buffer_size,
+                                    uint32_t* out_first_out_of_line,
+                                    const char** out_error);
 
 } // namespace fidl