Skip to content

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 memory
const 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);

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.

import { toRustReprC } from 'capnstruct/codegen';
import { SpdSample } from './schema.js';
console.log(toRustReprC([SpdSample]));
Terminal window
npx capnstruct ./src/schema.ts -o ./src/schema_generated.rs
// 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);
};