Sign in

    rkyv

    Safe MoonBit reader and small encoder for rkyv 0.8 archives

    rkyv
    serialization
    archive
    interoperability
    Download zip
    Author
    Version
    0.5.0
    License
    Apache-2.0
    Last updated
    2 months ago
    Downloads
    44

    No dependencies

    #mizchi/rkyv

    A small, safe MoonBit runtime and encoder for rkyv 0.8 archives. It reads a Rust-produced archive without copying it, while keeping schema-specific code in generated bindings or application code.

    The design follows the split used by cometkim/rkyv-js: a shared wire-format runtime plus bindings generated from Rust types.

    #Release contract

    The supported wire-format contract is rkyv 0.8.17 with its default format:

    • little-endian;
    • aligned archives; and
    • 32-bit relative pointers and archived usize values.

    The executable compatibility contract covers the checked reader primitives, host-defined scalar/vector/option/tagged-union schemas, and the generated catalog bindings. See conformance/README.md for the exact fixtures and limits.

    #Features

    • Bounds-checked reader for the default rkyv format.
    • Bit-compatible u8–u64, i8–i64, f32, f64, and bool reads.
    • Relative pointers, ArchivedVec<T> headers, and ArchivedOption<T> value offsets.
    • A bounds-checked, zero-copy BytesView for raw archive ranges.
    • A zero-copy ArchivedVec<u32> view with lazy access, Array/ FixedArray materialization, and caller-owned buffer copies.
    • Default-format schema-directed encoders for primitives, strings, vectors, options, structs, and explicit tagged unions.
    • Little- and big-endian primitive readers with 16-, 32-, or 64-bit pointers.
    • Experimental Rust code generation for typed MoonBit views of named structs.
    • Experimental MoonBit-owned schemas for Rust-free default-format encoding and schema-directed zero-copy views, including the JavaScript target.
    • Optional RMBT v1 envelopes with format flags, payload length, CRC-32, and a caller-provided payload limit for untrusted transport data.

    #Usage

    import {
    "mizchi/rkyv" @rkyv,
    }

    let reader = @rkyv.Reader::new(archive_bytes)
    let view = reader.read_vec_u32(archive_bytes.length() - 8) catch {
    error => abort("invalid rkyv archive: \{error}")
    }

    All fallible reader and generated-view APIs raise RkyvError. View::root is the lazy path for trusted archives. For an untrusted archive generated Rust views also provide View::validate(bytes): it traverses every supported field, checks pointers, collection spans, UTF-8, canonical bool/Option/enum tags, and limits recursive traversal to 256 levels before returning the same zero-copy view. It is deliberately scoped to the generated supported types and is not a drop-in replacement for Rust bytecheck on arbitrary rkyv types.

    #Transport envelope

    For bytes received from a file, network, or cache, wrap the ordinary rkyv payload in an optional RMBT v1 envelope. It records the selected rkyv format, payload length, and CRC-32; decode_envelope_with_limit rejects an excessive declared payload before copying it. After checking the envelope, pass its payload to the generated validator.

    ///|
    let envelope = @rkyv.encode_envelope(archive)

    ///|
    let decoded = @rkyv.decode_envelope_with_limit(envelope, 16 * 1024 * 1024) catch {
    error => abort("invalid archive transport: \{error}")
    }

    ///|
    let view = @catalog.CatalogView::validate(decoded.payload_bytes()) catch {
    error => abort("invalid rkyv archive: \{error}")
    }

    The primary collection APIs are read_vec_u32, read_vec_u32_length, read_vec_u32_into, and U32VecView::copy_into. They validate first and do not allocate an error wrapper on a successful read:

    ///|
    let view = reader.read_vec_u32(root_offset) catch {
    error => abort("invalid rkyv archive: \{error}")
    }

    ///|
    let selected = view.get(index)

    Use try/catch when a caller needs local recovery. U32VecView::get and U32VecView::at return None only for an out-of-range logical index; archive validation failures always raise RkyvError.

    reader.read_bytes_view(offset, length) returns a zero-copy BytesView after checking only the requested range. It does not validate a higher-level rkyv layout or UTF-8; use read_string when a decoded, validated String is needed.

    #Generated MoonBit schemas and JavaScript

    Use an .mbtx script as the source of truth, generate a typed MoonBit package, then import that package from the client. This keeps the dynamic Schema / Value representation out of application code while still using no Rust.

    #Direct host-defined schema API

    For a small or one-off archive, define the schema directly in MoonBit. The field declaration order in Schema::Struct is the archive layout contract. Value::Struct checks both the field count and names before encoding, and raises SchemaError if it does not match the schema.

    This direct form is runtime-checked because field names and schemas are values. Use the generated API below when callers need compile-time field and value types.

    ///|
    let user = @rkyv.Schema::Struct([
    { name: "id", schema: @rkyv.Schema::U32 },
    { name: "active", schema: @rkyv.Schema::Bool },
    { name: "name", schema: @rkyv.Schema::String },
    { name: "scores", schema: @rkyv.Schema::VecU32 },
    ])

    ///|
    let archive = user.encode(
    @rkyv.Value::Struct([
    { name: "id", value: @rkyv.Value::U32(42U) },
    { name: "active", value: @rkyv.Value::Bool(true) },
    { name: "name", value: @rkyv.Value::String("Ada") },
    { name: "scores", value: @rkyv.Value::VecU32([7U, 11U, 13U]) },
    ]),
    ) catch {
    _ => abort("schema and value do not match")
    }

    root opens a schema-directed zero-copy View. Accessing the vector does not materialize an Array unless the caller asks for one:

    ///|
    let root = user.root(archive) catch { _ => abort("invalid archive") }

    ///|
    let name = root.field("name")

    ///|
    let scores = root.field("scores")

    #In-place archive updates

    Use encode_mut when the archive buffer must remain caller-owned. root_mut validates the root and provides MutView setters that never relocate data: set_u32, set_bool, set_string with the same UTF-8 byte length, and vec_u32_mut().set for an existing vector index. Changing a string or vector length still requires re-encoding the archive.

    ///|
    let archive = user.encode_mut(
    @rkyv.Value::Struct([
    { name: "id", value: @rkyv.Value::U32(42U) },
    { name: "active", value: @rkyv.Value::Bool(true) },
    { name: "name", value: @rkyv.Value::String("Ada") },
    { name: "scores", value: @rkyv.Value::VecU32([7U, 11U, 13U]) },
    ]),
    )

    ///|
    let root = user.root_mut(archive.mut_view())

    ///|
    match root.field("id") {
    Some(id) => ignore(id.set_u32(99U))
    None => abort("missing id")
    }

    ///|
    match root.field("scores") {
    Some(scores) => match scores.vec_u32_mut() {
    Some(scores) => ignore(scores.set(1, 42U))
    None => abort("scores is not Vec<u32>")
    }
    None => abort("missing scores")
    }

    ///|
    let user = {
    name: "User",
    fields: [
    { name: "id", typ: UInt32 },
    { name: "active", typ: Bool },
    { name: "name", typ: Text },
    { name: "scores", typ: UInt32Array },
    ],
    }
    emit_schema(user)

    The JavaScript client imports only the generated API. It receives schema-specific UserView and UserMutView types: callers cannot pass a field name, and the type of every getter and setter is fixed by the source schema. encode always returns caller-owned Array[Byte]; convert it explicitly with Bytes::from_array when opening a read-only view.

    ///|
    import {
    "mizchi/rkyv/examples/host_codegen/generated" @user,
    }

    ///|
    let archive = @user.encode(42U, true, "Ada", [7U, 11U, 13U])

    ///|
    let root = @user.UserView::root(Bytes::from_array(archive))

    ///|
    let name : String = root.name()

    ///|
    let scores : @rkyv.U32VecView = root.scores()

    For an in-place update, the generated mutable view exposes only valid setters:

    ///|
    let archive = @user.encode(42U, true, "Ada", [7U, 11U, 13U])

    ///|
    let writer = @user.UserMutView::root(archive.mut_view())

    ///|
    writer.set_id(99U)
    writer.set_active(false)
    let patched : Bool = writer.set_name_same_length("Eve")
    let scores = writer.scores_mut()
    ignore(scores.set(1, 42U))

    Regenerate after changing the source schema. host-js first confirms that the checked-in generated source is current, then runs the client entirely under the JS target:

    just host-schema just host-js # Eve: 3 scores

    The current generator supports u32, bool, String, and Vec<u32> fields. Field declaration order is the layout contract. It does not infer arbitrary Rust Archived<T> field offsets; for a schema shared with an existing Rust type, verify its bytes against Rust or use the Rust layout codegen path.

    #Dogfooding: static product catalog

    examples/catalog is an end-to-end, production-shaped example. A Rust build step owns Catalog { products: Vec<Product> }, archives real catalog data with rkyv, and uses RkyvMbt to generate the exact MoonBit views. The JavaScript-target client opens the checked-in .rkyv binary through a minimal Node Buffer -> Bytes loader, then searches the Vec<Product> lazily without parsing JSON or materializing a product array.

    The generated package also contains ProductInput and CatalogInput. Their constructors make the MoonBit-to-Rust path type-safe for this schema, including Vec<Product>, Vec<String>, and Option<u32>.

    just catalog-generate moon test --target js examples/catalog/client just catalog-js # Moon Mug: 1800 cents just catalog-roundtrip

    check-catalog, which is included in just conformance, verifies that the Rust types, generated views, and archive fixture remain synchronized. catalog-roundtrip writes a catalog through CatalogInput::encode() in MoonBit, then validates and inspects it with Rust rkyv::access.

    #Benchmarks

    Native release results for a 4,096-element Vec<u32>. The archive is built once outside each measured iteration. MoonBit numbers are benchmark means; Rust numbers are Criterion 95% confidence intervals, so treat this as an end-to-end comparison rather than a precise instruction-level comparison.

    Environment: arm64, macOS 26.5.2, Moon 0.1.20260713 / moonc 0.10.4, Rust 1.96.0, and rkyv 0.8.17.

    OperationMoonBit nativeRust rkyv native
    Checked header + length validation6.33 ns3.01 ns
    Validated lazy selected element6.84 ns1.44 ns
    Checked root + selected lazy element9.81 ns3.32 ns
    Checked eager materialization of all 4K elements478.40 ns256.22 ns
    Copy from a validated view to a reused FixedArray / Rust Vec200.39 ns200.15 ns
    Checked copy to a reused FixedArray / Rust Vec198.22 ns201.11 ns
    Copy to a reused MutArrayView1.50 µs—

    The Rust comparison uses rkyv::access, its public checked API. Both sides receive the same already-encoded 4K Vec<u32> archive; archive construction is outside every timed loop. MoonBit uses --target native; target runtimes, allocation strategy, and compiler optimization are all included in these numbers. Re-run on the target system before making a deployment decision:

    just bench just bench-profile just bench-compare just profile just host-js

    For throughput-sensitive code, retain a U32VecView for repeated get calls, use FixedArray with copy_into for reused storage, and prefer to_fixed_array_fast() to to_array_fast() when a growable Array is not required. MutArrayView keeps a convenient subrange but is not the native bulk-copy path.

    #Rust interoperability

    just conformance verifies both directions byte-for-byte:

    • MoonBit reads bytes generated by Rust rkyv::to_bytes.
    • Rust rkyv::access accepts bytes generated by MoonBit encode_vec_u32 and encode_string.
    • The catalog example proves generated MoonBit-to-Rust archives for nested structs, primitive vectors, Vec<String>, Option<Vec<T>>, and fieldless enums.

    Arbitrary #[derive(Archive)] structs, tuples, enums, generic collections, and general Vec<T> are outside this published compatibility guarantee.

    #Experimental typed code generation

    codegen/rust contains RkyvMbt, a Rust derive that asks the Rust compiler for the concrete Archived<T> size and field offsets, then renders a typed MoonBit view. It supports named structs with numeric primitives, bool, String, Vec<primitive>, Vec<String>, nested derive-enabled named structs, their vectors, and one-level Option<T> including Option<Vec<T>>. Fieldless enums generate a strict tag view and a type-safe writer through RkyvMbtEnum.

    render_moonbit_with_encoder() additionally generates TypeInput::new and TypeInput::encode for every supported struct field. The generated writer uses the Rust compiler's exact field offsets and Archived<T> size rather than recomputing struct layout from field declaration order.

    The Rust crates are experimental and are not published to crates.io. See codegen/rust/README.md for the supported profile and usage.

    #Development

    just fmt just check just test just conformance just codegen

    #License

    Apache-2.0. See LICENSE.

    EnvelopeError

    pub suberror EnvelopeError {
    InvalidMagic
    UnsupportedVersion(Byte)
    UnsupportedFormat(Byte)
    InvalidLength(Int)
    PayloadLimitExceeded(actual~ : Int, limit~ : Int)
    ChecksumMismatch(expected~ : UInt, actual~ : UInt)
    } derive(Eq,
    Debug
    )

    Envelope errors are intentionally separate from RkyvError: callers can reject a bad transport blob before attempting any schema-dependent reads.

    RkyvError

    pub suberror RkyvError {
    OutOfBounds(offset~ : Int, size~ : Int, length~ : Int)
    InvalidPointerWidth(Int)
    InvalidAlignment(Int)
    InvalidRelativePointer(Int)
    LengthOverflow(Int)
    InvalidCollectionLength(Int)
    InvalidElementSize(Int)
    DestinationTooSmall(required~ : Int, actual~ : Int)
    InvalidUtf8
    InvalidBool(Byte)
    InvalidOptionTag(Byte)
    InvalidUnionTag(Byte)
    DepthLimit(Int)
    } derive(Eq,
    Debug
    )

    Errors returned while safely reading an archive. This reader deliberately checks bounds, unlike rkyv's access_unchecked APIs.

    SchemaError

    pub(all) suberror SchemaError {
    TypeMismatch(expected~ : String)
    FieldCountMismatch(expected~ : Int, actual~ : Int)
    FieldMismatch(expected~ : String, actual~ : String)
    } derive(Eq,
    Debug
    )

    Errors raised by the host schema encoder before it emits an invalid archive.

    ArchiveEnvelope

    pub struct ArchiveEnvelope {
    version : Int
    format : Format
    payload : Bytes
    }

    The self-identifying wrapper used when an archive crosses a trust boundary. The contained payload is still a standard rkyv byte sequence: the wrapper is optional and is never part of a Rust Archived<T> layout.

    ArchiveEnvelope::archive_format

    fn ArchiveEnvelope::archive_format(self : ArchiveEnvelope) -> Format
    Returns the rkyv reader format declared by this envelope.

    ArchiveEnvelope::payload_bytes

    fn ArchiveEnvelope::payload_bytes(self : ArchiveEnvelope) -> Bytes
    Returns the validated rkyv payload owned by this envelope.

    Endian

    pub enum Endian {
    Little
    Big
    } derive(Eq,
    Debug
    )

    Byte order used by an rkyv archive.

    Field

    pub(all) struct Field {
    name : String
    schema : Schema
    }

    A named field in a host-defined archived struct.

    Format

    pub struct Format {
    endian : Endian
    pointer_width : Int
    aligned : Bool
    } derive(Eq,
    Debug
    )

    rkyv wire-format settings. The default is rkyv's standard aligned, little-endian, 32-bit-pointer format.

    Format::big_endian

    fn Format::big_endian(pointer_width : Int, aligned : Bool) -> Format

    Creates a big-endian rkyv format.

    Format::new

    fn Format::new(endian : Endian, pointer_width : Int, aligned : Bool) -> Format

    Creates a format explicitly. Use this when matching non-default rkyv feature flags in a Rust producer.

    Format::pointer_bytes

    fn Format::pointer_bytes(self : Format) -> Int raise RkyvError

    Returns the size of archived pointers and usize values in bytes.

    MutU32VecView

    pub struct MutU32VecView {
    bytes : MutArrayView[Byte]
    data_offset : Int
    length : Int
    }

    A mutable view of an existing ArchivedVec<u32>. It can replace elements in place, but cannot change the vector length or move its data.

    MutU32VecView::length

    fn MutU32VecView::length(self : MutU32VecView) -> Int

    Returns the fixed archived element count of this mutable vector view.

    MutU32VecView::set

    fn MutU32VecView::set(self : MutU32VecView, index : Int, value : UInt) -> Bool

    Replaces one existing vector element in place. Returns false for an out-of-range index; this API never resizes or relocates the vector.

    MutView

    pub struct MutView {
    schema : Schema
    bytes : MutArrayView[Byte]
    offset : Int
    }

    A schema-directed mutable view over a caller-owned archive buffer. It can update only fixed-width fields, so no relative pointer or collection length can become invalid.

    MutView::field

    fn MutView::field(self : MutView, name : String) -> MutView?

    Returns a nested mutable struct field by name. None means this view is not a struct or that the requested field does not exist.

    MutView::set_bool

    fn MutView::set_bool(self : MutView, value : Bool) -> Bool

    Replaces a boolean field in place. Returns false if this view's schema is not Schema::Bool.

    MutView::set_string

    fn MutView::set_string(self : MutView, value : String) -> Bool raise RkyvError

    Replaces a string only when its UTF-8 byte length is unchanged. The archive representation, relative pointer, and inline/out-of-line choice therefore remain valid. Returns false for a different-length string or a non-string schema; malformed archived strings raise RkyvError before any write.

    MutView::set_u32

    fn MutView::set_u32(self : MutView, value : UInt) -> Bool

    Replaces a u32 field in place. Returns false if this view's schema is not Schema::U32.

    MutView::vec_u32_mut

    fn MutView::vec_u32_mut(self : MutView) -> MutU32VecView? raise RkyvError

    Opens a mutable view of an existing Vec<u32>. The header and full element span are checked before returning the view. The snapshot used for validation is discarded; writes always target the caller-owned mutable buffer.

    Reader

    pub struct Reader {
    bytes : Bytes
    format : Format
    }

    A view over an rkyv archive. It keeps the caller's Bytes buffer and never copies data while resolving primitive values or relative pointers.

    Reader::bytes

    fn Reader::bytes(self : Reader) -> Bytes

    Returns the archive's backing bytes without copying them.

    Reader::new

    fn Reader::new(bytes : Bytes) -> Reader

    Creates a reader for rkyv's default format.

    Reader::read_bool

    fn Reader::read_bool(self : Reader, offset : Int) -> Bool raise RkyvError

    Reads rkyv's one-byte boolean representation.

    Reader::read_bool_strict

    fn Reader::read_bool_strict(self : Reader, offset : Int) -> Bool raise RkyvError

    Reads a boolean for an untrusted archive. Unlike read_bool, this rejects values other than rkyv's canonical 0 and 1 discriminants.

    Reader::read_bytes_view

    fn Reader::read_bytes_view(self : Reader, offset : Int, length : Int) -> BytesView raise RkyvError

    Returns a borrowed, bounds-checked range of the archive without copying. This is intended for archived byte blobs and integrations that can consume raw UTF-8 bytes directly. It does not validate a higher-level rkyv layout or UTF-8; use read_string when a decoded, validated String is needed.

    Reader::read_f32

    fn Reader::read_f32(self : Reader, offset : Int) -> Float raise RkyvError

    Reads an IEEE 754 single-precision float without changing its bit pattern.

    Reader::read_f64

    fn Reader::read_f64(self : Reader, offset : Int) -> Double raise RkyvError

    Reads an IEEE 754 double-precision float without changing its bit pattern.

    Reader::read_i16

    fn Reader::read_i16(self : Reader, offset : Int) -> Int raise RkyvError

    Reads a signed 16-bit integer.

    Reader::read_i32

    fn Reader::read_i32(self : Reader, offset : Int) -> Int raise RkyvError

    Reads a signed 32-bit integer.

    Reader::read_i64

    fn Reader::read_i64(self : Reader, offset : Int) -> Int64 raise RkyvError

    Reads a signed 64-bit integer.

    Reader::read_i8

    fn Reader::read_i8(self : Reader, offset : Int) -> Int raise RkyvError

    Reads a signed 8-bit integer.

    Reader::read_option_value_offset

    fn Reader::read_option_value_offset(self : Reader, offset : Int, value_alignment : Int) -> Int? raise RkyvError

    Resolves an ArchivedOption<T> tag into the offset of its present value. The caller supplies the archived alignment of T; this keeps type-specific layout in generated bindings while centralizing rkyv's tag/padding rule.

    Reader::read_option_value_offset_strict

    fn Reader::read_option_value_offset_strict(self : Reader, offset : Int, value_alignment : Int) -> Int? raise RkyvError

    Resolves an ArchivedOption<T> for an untrusted archive and rejects tags other than rkyv's canonical 0 (None) and 1 (Some) values.

    Reader::read_rel_ptr

    fn Reader::read_rel_ptr(self : Reader, offset : Int) -> Int raise RkyvError

    Resolves an ordinary rkyv relative pointer. ArchivedString is the one exception: its pointer is relative to the string representation's start.

    Reader::read_rel_ptr_offset

    fn Reader::read_rel_ptr_offset(self : Reader, offset : Int) -> Int raise RkyvError

    Reads the signed raw offset stored in an rkyv relative pointer.

    Reader::read_string

    fn Reader::read_string(self : Reader, offset : Int) -> String raise RkyvError

    Reads rkyv 0.8's hybrid inline/out-of-line ArchivedString layout.

    Reader::read_u16

    fn Reader::read_u16(self : Reader, offset : Int) -> UInt raise RkyvError

    Reads an unsigned 16-bit integer using the archive's configured endianness.

    Reader::read_u32

    fn Reader::read_u32(self : Reader, offset : Int) -> UInt raise RkyvError

    Reads an unsigned 32-bit integer using the archive's configured endianness.

    Reader::read_u64

    fn Reader::read_u64(self : Reader, offset : Int) -> UInt64 raise RkyvError

    Reads an unsigned 64-bit integer using the archive's configured endianness.

    Reader::read_u8

    fn Reader::read_u8(self : Reader, offset : Int) -> Byte raise RkyvError

    Reads an unsigned 8-bit integer.

    Reader::read_union_tag

    fn Reader::read_union_tag(self : Reader, offset : Int, accepted : Array[Byte]) -> Byte raise RkyvError

    Reads a #[repr(u8)] enum tag for an untrusted archive. The accepted tags are supplied by generated bindings; an unknown value is raised from inside this package because RkyvError is intentionally a read-only suberror to downstream MoonBit packages.

    Reader::read_usize

    fn Reader::read_usize(self : Reader, offset : Int) -> Int raise RkyvError

    Reads rkyv's fixed-width archived usize. Offsets larger than MoonBit's addressable Int range are rejected instead of wrapping.

    Reader::read_vec_header

    fn Reader::read_vec_header(self : Reader, offset : Int) -> VecHeader raise RkyvError

    Reads the header of an ArchivedVec<T>. Element decoding remains a schema concern and belongs in generated or hand-written bindings.

    Reader::read_vec_header_with_element_size

    fn Reader::read_vec_header_with_element_size(self : Reader, offset : Int, element_size : Int) -> VecHeader raise RkyvError

    Reads an ArchivedVec<T> header and validates its complete element range. Generated bindings supply the archived element size measured by the Rust compiler, preventing a corrupted length from escaping as a lazy view.

    Reader::read_vec_u32

    fn Reader::read_vec_u32(self : Reader, offset : Int) -> U32VecView raise RkyvError

    Reads an ArchivedVec<u32> as a zero-copy typed view. The whole byte span is validated before returning so subsequent indexing is bounded by the archive rather than a potentially corrupted archived length.

    Reader::read_vec_u32_into

    fn Reader::read_vec_u32_into(self : Reader, offset : Int, destination : FixedArray[UInt]) -> Int raise RkyvError

    Validates an ArchivedVec<u32> and copies it into caller-owned fixed-size storage. This convenience API does not expose or retain a view; a short destination is left untouched and returns DestinationTooSmall. On success, it returns the copied element count for making an ArrayView.

    Reader::read_vec_u32_length

    fn Reader::read_vec_u32_length(self : Reader, offset : Int) -> Int raise RkyvError

    Validates an ArchivedVec<u32> and returns only its element count. Unlike read_vec_u32, this does not create a U32VecView or retain the archive. Use it for header-only consumers; use read_vec_u32 when reading elements.

    Reader::root_offset

    fn Reader::root_offset(self : Reader, size : Int) -> Int raise RkyvError

    Gets the byte offset of a root whose archived representation has size bytes. rkyv writes dependencies first and puts the root at the end.

    Reader::validate_range

    fn Reader::validate_range(self : Reader, offset : Int, size : Int) -> Unit raise RkyvError

    Validates that a fixed-size archived value is wholly contained in the input. Generated nested views use this before retaining an inline offset.

    Reader::with_format

    fn Reader::with_format(bytes : Bytes, format : Format) -> Reader

    Creates a reader with an explicitly selected rkyv format.

    Schema

    pub(all) enum Schema {
    U8
    U16
    U32
    U64
    I8
    I16
    I32
    I64
    F32
    F64
    Bool
    String
    VecU32
    Vec(Schema)
    Option(Schema)
    Struct(Array[Field])
    StructLayout(Array[Field], Array[Int], Int)
    TaggedUnion(Array[Variant], Int, Int, Int)
    }

    A MoonBit-owned schema for the rkyv 0.8 default archive profile. It is a small, explicit host-side alternative to the Rust layout derive. Struct uses field order as its contract; generated Rust writers use StructLayout with compiler-provided offsets and archived size instead.

    Schema::encode

    fn Schema::encode(self : Schema, value : Value) -> Bytes raise SchemaError

    Encodes a value into an immutable rkyv archive. Use encode_mut when the caller intends to apply later fixed-width in-place updates.

    Schema::encode_mut

    fn Schema::encode_mut(self : Schema, value : Value) -> Array[Byte] raise SchemaError

    Encodes a host-defined value into a caller-owned mutable rkyv archive. This function is target-independent, so moon build --target js produces the same bytes without Rust.

    Schema::root

    fn Schema::root(self : Schema, bytes : Bytes) -> View raise RkyvError

    Opens the root representation described by this host schema. The complete inline span is validated immediately; out-of-line values are validated by their respective reader accessors.

    Schema::root_mut

    fn Schema::root_mut(self : Schema, bytes : MutArrayView[Byte]) -> MutView raise RkyvError

    Opens a mutable root over caller-owned archive bytes. The inline root span is validated before the view is returned; collection fields are additionally validated when vec_u32_mut is requested.

    Schema::validate

    fn Schema::validate(self : Schema, bytes : Bytes) -> Unit raise RkyvError

    Fully validates a host-defined archive before it is exposed to an application. This checks every reachable relative pointer, string, vector extent, option tag, boolean discriminant, and tagged-union discriminant.

    U32VecView

    pub struct U32VecView {
    bytes : Bytes
    format : Format
    data_offset : Int
    length : Int
    }

    A zero-copy typed view of an ArchivedVec<u32>. Generated bindings can retain this view and resolve only the elements requested by their caller.

    U32VecView::at

    fn U32VecView::at(self : U32VecView, index : Int) -> UInt?

    Resolves one element without copying the rest of the archived vector. An index outside the vector's logical range returns None.

    U32VecView::copy_into

    fn U32VecView::copy_into(self : U32VecView, destination : FixedArray[UInt]) -> Unit raise RkyvError

    Copies every element into caller-owned fixed-size storage. The view has already validated the archived span. A destination that is too short is left untouched so callers can safely reuse it after an error.

    U32VecView::copy_into_mut_view

    fn U32VecView::copy_into_mut_view(self : U32VecView, destination : MutArrayView[UInt]) -> Unit raise RkyvError

    Copies every element into a caller-owned mutable array window. The window can start at an arbitrary offset in either an Array or FixedArray; it uses v128 loads and scalar stores rather than the native FixedArray bulk-copy FFI.

    U32VecView::data_byte_offset

    fn U32VecView::data_byte_offset(self : U32VecView) -> Int

    Returns the byte offset of the first u32 element within the archive. Integrations that store an entire archive in 32-bit shared memory can convert this to a word offset after checking that it is divisible by four.

    U32VecView::get

    fn U32VecView::get(self : U32VecView, index : Int) -> UInt?

    Resolves an element from a fully validated archived vector. read_vec_u32 verifies the complete element span before constructing this view, so a valid in-range index cannot fail byte-range validation. Out-of-range indices return None.

    U32VecView::length

    fn U32VecView::length(self : U32VecView) -> Int

    Returns the number of elements in the archived vector without decoding it.

    U32VecView::to_array

    fn U32VecView::to_array(self : U32VecView) -> Array[UInt]

    Decodes every element into a MoonBit array. Keep the U32VecView when a caller only needs selected elements; use this at API boundaries that need an owned collection.

    U32VecView::to_array_fast

    fn U32VecView::to_array_fast(self : U32VecView) -> Array[UInt]

    Decodes every element after read_vec_u32 has validated the full element span. Unlike the compatibility wrapper, this cannot fail.

    U32VecView::to_fixed_array_fast

    fn U32VecView::to_fixed_array_fast(self : U32VecView) -> FixedArray[UInt]

    Decodes every element after validation into a fixed-size array. This avoids the extra allocation and copy required to return a growable Array.

    Value

    pub(all) enum Value {
    U8(Byte)
    U16(UInt)
    U32(UInt)
    U64(UInt64)
    I8(Int)
    I16(Int)
    I32(Int)
    I64(Int64)
    F32(Float)
    F64(Double)
    Bool(Bool)
    String(String)
    VecU32(Array[UInt])
    Vec(Array[Value])
    Option(Value?)
    Struct(Array[ValueField])
    Tagged(Byte, Value?)
    }

    A dynamic value accepted by a Schema encoder.

    ValueField

    pub(all) struct ValueField {
    name : String
    value : Value
    }

    A named field value. Names make accidental schema/value ordering mistakes fail before any root archive object is written.

    Variant

    pub(all) struct Variant {
    name : String
    tag : Byte
    schema : Schema
    }

    One variant in a host-defined #[repr(u8)] archived enum. tag_offset, payload_offset, and archived_size are supplied by TaggedUnion, so the union's native Rust layout is always an explicit part of the contract.

    VecHeader

    pub struct VecHeader {
    data_offset : Int
    length : Int
    } derive(Eq,
    Debug
    )

    The two fields that make up an ArchivedVec<T> header.

    View

    pub struct View {
    schema : Schema
    reader : Reader
    offset : Int
    }

    A schema-directed, zero-copy view over a host-defined archived value. Field lookup stays dynamic because the schema is a MoonBit value rather than source generated from Rust's compiler layout.

    View::field

    fn View::field(self : View, name : String) -> View?

    Returns a nested struct field by name. None means this view is not a struct or that the requested field does not exist in its host schema.

    View::option_value

    fn View::option_value(self : View) -> View? raise RkyvError

    Opens the present value of a host-defined Option<T>. None means either this view is not optional or the archived value is absent.

    View::read_bool

    fn View::read_bool(self : View) -> Bool? raise RkyvError

    Reads a boolean when this view's schema is Schema::Bool.

    View::read_f32

    fn View::read_f32(self : View) -> Float? raise RkyvError

    Reads an f32 when this view's schema is Schema::F32.

    View::read_f64

    fn View::read_f64(self : View) -> Double? raise RkyvError

    Reads an f64 when this view's schema is Schema::F64.

    View::read_i16

    fn View::read_i16(self : View) -> Int? raise RkyvError

    Reads an i16 when this view's schema is Schema::I16.

    View::read_i32

    fn View::read_i32(self : View) -> Int? raise RkyvError

    Reads an i32 when this view's schema is Schema::I32.

    View::read_i64

    fn View::read_i64(self : View) -> Int64? raise RkyvError

    Reads an i64 when this view's schema is Schema::I64.

    View::read_i8

    fn View::read_i8(self : View) -> Int? raise RkyvError

    Reads an i8 when this view's schema is Schema::I8.

    View::read_string

    fn View::read_string(self : View) -> String? raise RkyvError

    Reads a string when this view's schema is Schema::String.

    View::read_tag

    fn View::read_tag(self : View) -> Byte? raise RkyvError

    Reads an explicit tagged-union discriminant without resolving its payload.

    View::read_u16

    fn View::read_u16(self : View) -> UInt? raise RkyvError

    Reads a u16 when this view's schema is Schema::U16.

    View::read_u32

    fn View::read_u32(self : View) -> UInt? raise RkyvError

    Reads a u32 when this view's schema is Schema::U32.

    View::read_u64

    fn View::read_u64(self : View) -> UInt64? raise RkyvError

    Reads a u64 when this view's schema is Schema::U64.

    View::read_u8

    fn View::read_u8(self : View) -> Byte? raise RkyvError

    Reads a u8 when this view's schema is Schema::U8.

    View::read_vec_i16

    fn View::read_vec_i16(self : View) -> Array[Int]? raise RkyvError

    Materializes a generic ArchivedVec<i16> after validating its complete span. The dedicated lazy U32VecView remains available for the hot u32 path; other primitive vectors favor a compact general implementation.

    View::read_vec_u32

    fn View::read_vec_u32(self : View) -> U32VecView? raise RkyvError

    Opens a zero-copy ArchivedVec<u32> view when this schema declares one.

    View::read_vec_u64

    fn View::read_vec_u64(self : View) -> Array[UInt64]? raise RkyvError

    Materializes a generic ArchivedVec<u64> after validating its complete span. It is useful for host-side inspection and test conformance; generated bindings use lazy per-field views for all primitive vectors.

    View::tagged_value

    fn View::tagged_value(self : View) -> View? raise RkyvError

    Opens the active explicit tagged-union payload. Unknown tags are rejected rather than treated as absent, which keeps untrusted validation strict.

    crc32

    fn crc32(bytes : Bytes) -> UInt

    Computes the IEEE CRC-32 used by the envelope. This small table-free form keeps the JavaScript runtime dependency-free and has negligible cost for the small headers and metadata archives this package targets.

    decode_envelope

    fn decode_envelope(bytes : Bytes) -> ArchiveEnvelope raise EnvelopeError

    Parses and checks a v1 envelope before returning a standalone rkyv payload. Applications should pass decoded.archive_format() to Reader::with_format when the producer may select non-default rkyv format features.

    decode_envelope_with_limit

    fn decode_envelope_with_limit(bytes : Bytes, max_payload_length : Int) -> ArchiveEnvelope raise EnvelopeError

    Parses an envelope while enforcing a caller-provided payload ceiling before copying any payload bytes. Use this at network and storage boundaries to keep malformed length headers from causing unbounded allocation.

    default_format

    fn default_format() -> Format

    Returns the format used by rkyv when no format feature is enabled.

    encode_envelope

    fn encode_envelope(payload : Bytes) -> Bytes

    Wraps a default-format rkyv payload in the versioned envelope. Use encode_envelope_with_format when Rust was built with a non-default rkyv format feature.

    encode_envelope_with_format

    fn encode_envelope_with_format(payload : Bytes, format : Format) -> Bytes raise EnvelopeError

    Wraps an rkyv payload with its exact reader format. The payload is copied once so the returned value is a standalone transport buffer.

    encode_string

    fn encode_string(value : String) -> Bytes

    Encodes a root String in rkyv 0.8's default format. Strings up to eight UTF-8 bytes are inline; longer strings are written before their root repr.

    encode_vec_u32

    fn encode_vec_u32(values : Array[UInt]) -> Bytes

    Encodes a root Vec<u32> in rkyv's default format. The element data is emitted before the ArchivedVec root header, as required by rkyv.

    envelope_header_size

    let envelope_header_size : Int

    Number of bytes before the rkyv payload starts.

    envelope_version

    let envelope_version : Byte

    The current envelope protocol version.

    require_validation_depth

    fn require_validation_depth(remaining_depth : Int) -> Unit raise RkyvError

    Enforces the recursion limit used by generated full-archive validators. The constructor stays inside this package because RkyvError is a suberror and cannot be constructed by generated client packages.