ark-api-ffi 0.16.0

Ark low-level Wasm FFI API
Documentation
use crate::ErrorCode;
use bytemuck::CheckedBitPattern;
use bytemuck::NoUninit;
use num_enum::IntoPrimitive;
use num_enum::TryFromPrimitive;

// 3. By applying #[repr(C)] we're telling Rust that we want to layout `IsReady`
// exactly as if this struct was declared in C itself.
//
// By deriving the `NoUninit` and `CheckedBitPattern` traits, we are adding compile-time
// checks that our type meets a few important invariants that all FFI types must meet.
// More about this later.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct IsReady {
    pub is_ready: bool,
    // More details on why this is necessary later, but basically, if this wasn't
    // here then there would be padding bytes in this struct layout so that `size`
    // could be aligned. We take those padding bytes up with "explicit"/"manual" padding
    // here instead, so that we can satisfy the requirements of `NoUninit`
    pub _pad: [u8; 3],
    pub size: u32,
}

// 4. By applying #[repr(u32)] we're telling Rust that we want `State` to
// be represented as a primitive. `NoUninit`, `CheckedBitPattern`,
// `IntoPrimitive` and `TryFromPrimitive` are needed as well to safely convert/cast
// between the primitive type and the enum.
#[repr(u32)]
#[derive(Clone, Copy, IntoPrimitive, TryFromPrimitive, NoUninit, CheckedBitPattern)]
pub enum State {
    SomeStateX,
    SomeStateY,
}

#[link(wasm_import_module = "example-manual")]
extern "C" {
    // We need to rename the function to add a prefix. This is standard in
    // most larger C APIs to avoid name clashes in the presence of other
    // external code. In `ark`, the convention is the snake case name of the
    // API you are exposing, joined with the "normal" function name by `__`
    pub fn example__is_ready(
        // 1. &str is a ultimately an array of bytes, so we need to pull apart
        // the string into FFI compatible components, which are the pointer
        // to the beginning of that array, and the length of it.
        //
        // Note that since Wasm is 32-bit only (atm), we use `u32` for lengths
        // that in Rust would normally be usize, the size of the pointer for
        // the target architecture
        name_ptr: *const u8,
        name_len: u32,
        // This one is fine!
        max: u32,
        // 4. As `State` is being represented as a `u32` primitive type we use `u32`
        // as the function parameter.
        state: u32,
        // 2. Another limitation of the C ABI (but not wasm actually) is that
        // you can only have a single return value. Since we want to use a
        // Result of either an Ok(IsReady) or Err(ErrorCode) we are kind of
        // stuck since we have to get rid of the Result since we can't represent
        // it in the C ABI.
        //
        // So, we instead change the signature entirely, and always return an
        // `ErrorCode` (which has a Success value for Ok), but then send a
        // mutable pointer to an `IsReady` that the host side can fill out to
        // give us an additional return value
        is_ready_out_ptr: *mut IsReady,
    ) -> ErrorCode;
}