Skip to main content

reifydb_extension/transform/
wasm.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4//! WASM transform implementation that executes WebAssembly modules as columnar transforms
5
6use reifydb_core::value::column::columns::Columns;
7use reifydb_sdk::marshal::wasm::{marshal_columns_to_bytes, unmarshal_columns_from_bytes};
8use reifydb_type::Result;
9
10use super::{Transform, context::TransformContext};
11use crate::loader::wasm::invoke_wasm_module;
12
13/// WASM transform that loads and executes a `.wasm` module.
14///
15/// Each WASM module must export:
16/// - `alloc(size: i32) -> i32` — allocate `size` bytes, return pointer
17/// - `dealloc(ptr: i32, size: i32)` — free memory
18/// - `transform(input_ptr: i32, input_len: i32) -> i32` — pointer to output (first 4 bytes at output pointer = output
19///   length as LE u32)
20pub struct WasmTransform {
21	name: String,
22	wasm_bytes: Vec<u8>,
23}
24
25impl WasmTransform {
26	pub fn new(name: impl Into<String>, wasm_bytes: Vec<u8>) -> Self {
27		Self {
28			name: name.into(),
29			wasm_bytes,
30		}
31	}
32
33	pub fn name(&self) -> &str {
34		&self.name
35	}
36}
37
38// SAFETY: WasmTransform only holds inert data (name + bytes).
39// A fresh Engine is created per invocation, so no shared mutable state.
40unsafe impl Send for WasmTransform {}
41unsafe impl Sync for WasmTransform {}
42
43impl Transform for WasmTransform {
44	fn apply(&self, _ctx: &TransformContext, input: Columns) -> Result<Columns> {
45		let input_bytes = marshal_columns_to_bytes(&input);
46		let label = format!("WASM transform '{}'", self.name);
47
48		let output_bytes = invoke_wasm_module(&self.wasm_bytes, "transform", &input_bytes, &label)?;
49
50		Ok(unmarshal_columns_from_bytes(&output_bytes))
51	}
52}