WebAssembly & Rust
1. WasmArena & WebAssembly.Memory.grow() Auto-Rebinding
Section titled “1. WasmArena & WebAssembly.Memory.grow() Auto-Rebinding”When sharing memory with a WebAssembly module (WebAssembly.Memory), calling memory.grow() or allocating inside a growable arena detaches the old ArrayBuffer, invalidating standard JavaScript DataView and TypedArray instances.
WasmArena bump-allocates aligned structs, arrays, and variable-length tail strings/slices directly inside WebAssembly.Memory (or a growable ArrayBuffer). Every StructView and StructArrayView created from the arena tracks the arena and automatically re-binds to the new ArrayBuffer whenever memory.grow() is called:
import { WasmArena, defineStruct, u32, f32, str } from 'capnstruct';
const QpPoint = defineStruct('QpPoint', { x: f32, y: f32, origIdx: u32, label: str,});
// Create a 1-page (64 KiB) WebAssembly.Memory and wrap it in WasmArena (starting at offset 64)const wasmMemory = new WebAssembly.Memory({ initial: 1 });const arena = new WasmArena(wasmMemory, 64);
// Allocate a 1,000-element StructArrayView inside WASM linear memoryconst points = arena.alloc(QpPoint, 1000);points.at(0).set({ x: 1.5, y: -2.5, origIdx: 7, label: 'radar-0' });
// Even if WASM or JS grows WebAssembly.Memory (detaching the old ArrayBuffer),// existing StructView and StructArrayView instances transparently re-bind!wasmMemory.grow(4);console.log(points.at(0).x, points.at(0).label); // 1.5, 'radar-0'
// Pass byteOffset and length directly to your exported WASM kernel:// wasmExports.process_points(points.byteOffset, points.length);2. Rust #[repr(C)] Codegen & CLI
Section titled “2. Rust #[repr(C)] Codegen & CLI”capnstruct/codegen and the capnstruct CLI generate exact Rust #[repr(C)] struct definitions—including CapnStr, CapnSlice<T>, explicit _padN alignment padding fields, and core::mem::{size_of, align_of, offset_of!} compile-time assertions—directly from your TypeScript schemas.
Programmatic API
Section titled “Programmatic API”import { toRustReprC } from 'capnstruct/codegen';import { SpdSample } from './schema.js';
console.log(toRustReprC([SpdSample]));CLI Usage
Section titled “CLI Usage”npx capnstruct ./src/schema.ts -o ./src/schema_generated.rsGenerated Rust Output
Section titled “Generated Rust Output”// Auto-generated by capnstruct. DO NOT EDIT.#![allow(dead_code, non_camel_case_types, non_snake_case)]
#[repr(C)]#[derive(Copy, Clone, Debug, PartialEq, Eq)]pub struct CapnStr { pub rel_offset: u32, pub len: u32,}
#[repr(C)]#[derive(Copy, Clone, Debug, PartialEq, Eq)]pub struct CapnSlice<T> { pub rel_offset: u32, pub len: u32, pub _marker: core::marker::PhantomData<*const T>,}
#[repr(C)]#[derive(Copy, Clone, Debug)]pub struct SpdSample { pub t: u32, pub _pad0: [u8; 4], pub val: f64, pub id: u32, pub _pad1: [u8; 4],}
const _: () = { assert!(core::mem::size_of::<SpdSample>() == 24); assert!(core::mem::align_of::<SpdSample>() == 8); assert!(core::mem::offset_of!(SpdSample, t) == 0); assert!(core::mem::offset_of!(SpdSample, val) == 8); assert!(core::mem::offset_of!(SpdSample, id) == 16);};