# fizzyx
Rust bindings for the [Fizzy] WebAssembly interpreter.
[Fizzy] is a small, fast, deterministic interpreter implementing the WebAssembly
1.0 (MVP) specification. `fizzyx` wraps Fizzy's C API in a safe, ergonomic Rust
interface inspired by [`wasmi`] and [`wasmtime`].
## Crates
| `fizzyx` | Safe, high-level API (`Engine`, `Module`, `Linker`, `Instance`, `Func`, …). |
| `fizzyx-sys` | Low-level `unsafe` FFI bindings generated with [bindgen]. |
Most users only need `fizzyx`.
## Quick start
```rust
use fizzyx::{Engine, Linker, Module, Val};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let wat = r#"(module (func (export "add") (param i32 i32) (result i32)
local.get 0
local.get 1
i32.add))"#;
let wasm = wat::parse_str(wat)?;
let engine = Engine::default();
let module = Module::new(&wasm)?;
let linker = Linker::new(&engine);
let mut instance = linker.instantiate(&module)?;
let add = instance.get_func("add").expect("missing export");
let mut results = [Val::I32(0)];
add.call(&mut instance, &[Val::I32(1), Val::I32(2)], &mut results)?;
assert_eq!(results[0], Val::I32(3));
Ok(())
}
```
Host functions are defined on the `Linker` before instantiation:
```rust
use fizzyx::{FuncType, Val, ValType};
linker.func_new(
"env",
"mul",
FuncType::new([ValType::I32, ValType::I32], [ValType::I32]),
|params, results| {
let a = params[0].i32().unwrap();
let b = params[1].i32().unwrap();
results[0] = Val::I32(a * b);
},
)?;
```
## Differences from `wasmtime` / `wasmi`
The API follows Fizzy's own model instead of forcing an exact match:
- **At most one result.** Fizzy targets WebAssembly 1.0, so functions return zero
or one value.
- **No `Store`.** Fizzy instances are self-contained — they own their memory and
globals — so there is no separate store. Methods that mutate an instance take
`&mut Instance` directly.
- **Instantiate-time imports.** Imports are resolved by `module::name` when a
module is instantiated through a `Linker`. Only function imports are currently
supported; instantiating a module that imports a memory, table, or global
fails.
- **No fuel/metering.** Fizzy's metered execution context is not yet exposed.
## Building
`fizzyx-sys` builds Fizzy from source, vendored as a git submodule. After cloning:
```sh
git submodule update --init --recursive
cargo build
```
Requirements:
- A **C++17 toolchain** and **CMake** (≥ 3.15) to build Fizzy.
- **libclang** for `bindgen`.
Fizzy stores its byte buffers as `std::basic_string<uint8_t>`. Modern libc++
(≥ 19, e.g. the Xcode 16 SDK) no longer provides `std::char_traits<unsigned char>`,
so the build script force-includes a small shim
([`crates/fizzyx-sys/shim/char_traits_shim.hpp`](crates/fizzyx-sys/shim/char_traits_shim.hpp))
that supplies it. The shim is a no-op on libstdc++.
## Vendored sources and updating Fizzy
Fizzy's repository root ships its own `Cargo.toml`, which makes Cargo treat the
submodule as a foreign package and exclude it from published tarballs. So
`fizzyx-sys` builds and publishes from a committed, `Cargo.toml`-free mirror of the
sources it needs at [`crates/fizzyx-sys/fizzy-vendored/`](crates/fizzyx-sys/fizzy-vendored/).
`build.rs` regenerates that mirror from the submodule on every build, so you never
edit it by hand.
To update Fizzy:
```sh
git -C crates/fizzyx-sys/fizzy checkout <new-commit> # bump the submodule
cargo build -p fizzyx-sys # regenerates fizzy-vendored/
git add crates/fizzyx-sys/fizzy crates/fizzyx-sys/fizzy-vendored
git commit -m "update Fizzy"
```
CI fails if `fizzy-vendored/` is out of sync with the submodule.
## License
Licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
- MIT license ([LICENSE-MIT](LICENSE-MIT))
at your option. The vendored Fizzy sources are licensed under Apache-2.0.
[Fizzy]: https://github.com/wasmx/fizzy
[bindgen]: https://github.com/rust-lang/rust-bindgen
[`wasmi`]: https://docs.rs/wasmi
[`wasmtime`]: https://docs.rs/wasmtime