| Crates.io | surrealism-types |
| lib.rs | surrealism-types |
| version | 0.1.7 |
| created_at | 2025-11-05 15:26:38.060488+00 |
| updated_at | 2026-01-16 21:35:22.834673+00 |
| description | Types for Surrealism |
| homepage | https://github.com/surrealdb/surrealdb |
| repository | https://github.com/surrealdb/surrealdb |
| max_upload_size | |
| id | 1918156 |
| size | 97,037 |
A language-agnostic serialization framework for WebAssembly (WASM) guest-host communication in SurrealDB's Surrealism ecosystem.
surrealism-types provides a custom binary serialization protocol designed to enable SurrealDB client modules to be built in any language that compiles to WebAssembly. By defining a well-specified wire format and memory transfer protocol, this crate allows different programming languages to communicate seamlessly across WASM boundaries.
Instead of using standard serialization frameworks like Serde with JSON or bincode, surrealism-types implements a custom protocol for several key reasons:
bytes::Bytes to minimize memory allocationstransfer.rs)The foundation of cross-boundary communication:
// Guest side (sync)
pub trait Transfer {
fn transfer(self, controller: &mut dyn MemoryController) -> Result<Ptr>;
fn receive(ptr: Ptr, controller: &mut dyn MemoryController) -> Result<Self>;
}
// Host side (async)
#[async_trait]
pub trait AsyncTransfer {
async fn transfer(self, controller: &mut dyn AsyncMemoryController) -> Result<Ptr>;
async fn receive(ptr: Ptr, controller: &mut dyn AsyncMemoryController) -> Result<Self>;
}
Ptr: Type-safe wrapper around u32 pointers for WASM linear memoryTransfer: Synchronous trait for WASM guest-side operationsAsyncTransfer: Asynchronous trait for host-side (Wasmtime runtime) operationscontroller.rs)Abstracts memory allocation across guest and host:
// Guest side (sync)
pub trait MemoryController {
fn alloc(&mut self, len: u32, align: u32) -> Result<u32>;
fn free(&mut self, ptr: u32, len: u32) -> Result<()>;
fn mut_mem(&mut self, ptr: u32, len: u32) -> &mut [u8];
}
// Host side (async)
#[async_trait]
pub trait AsyncMemoryController {
async fn alloc(&mut self, len: u32, align: u32) -> Result<u32>;
async fn free(&mut self, ptr: u32, len: u32) -> Result<()>;
fn mut_mem(&mut self, ptr: u32, len: u32) -> &mut [u8];
}
serialize.rs)The core binary protocol:
pub trait Serializable: Sized {
fn serialize(self) -> Result<Serialized>;
fn deserialize(serialized: Serialized) -> Result<Self>;
}
All serialized data follows a length-prefixed pattern when transferred:
[4-byte length (u32, little-endian)][data bytes]
| Type | Format | Size |
|---|---|---|
String |
Raw UTF-8 bytes | Variable |
f64 |
Little-endian IEEE 754 | 8 bytes |
u64 |
Little-endian unsigned | 8 bytes |
i64 |
Little-endian signed | 8 bytes |
bool |
Single byte (0=false, 1=true) | 1 byte |
() |
Empty | 0 bytes |
All enums use a tag byte followed by optional payload:
Option<T>Some(T): [0x00][serialized T]
None: [0x01]
Result<T, E>Ok(T): [0x00][serialized T]
Err(E): [0x01][serialized E]
Bound<T>Unbounded: [0x00]
Included(T): [0x01][serialized T]
Excluded(T): [0x02][serialized T]
Vec<T>[4-byte count (u32)]
[4-byte item1_len][item1_data]
[4-byte item2_len][item2_data]
...
[4-byte elem1_len][elem1_data]
[4-byte elem2_len][elem2_data]
...
SerializableRange<T>[4-byte beg_len][4-byte end_len][beg_data][end_data]
surrealdb_types::Value and KindThese types use FlatBuffers serialization via the surrealdb-protocol crate:
[FlatBuffers-encoded data]
This leverages the existing SurrealDB binary protocol for complex database types like records, geometries, and durations.
hostEnable this feature for host-side (runtime) code:
[dependencies]
surrealism-types = { version = "*", features = ["host"] }
When enabled:
AsyncTransfer, AsyncMemoryController)Send bounds for thread-safe async operationsWhen disabled (default):
use surrealism_types::{Serializable, Transfer, MemoryController};
// Serialize and transfer a value across WASM boundary
fn export_value(value: String, controller: &mut dyn MemoryController) -> Result<u32> {
let ptr = value.transfer(controller)?;
Ok(*ptr)
}
// Receive and deserialize a value from host
fn import_value(ptr: u32, controller: &mut dyn MemoryController) -> Result<String> {
String::receive(ptr.into(), controller)
}
use surrealism_types::{Serializable, AsyncTransfer, AsyncMemoryController};
// Transfer a value to WASM module
async fn send_to_guest(
value: String,
controller: &mut dyn AsyncMemoryController
) -> Result<u32> {
let ptr = value.transfer(controller).await?;
Ok(*ptr)
}
// Receive a value from WASM module
async fn receive_from_guest(
ptr: u32,
controller: &mut dyn AsyncMemoryController
) -> Result<String> {
String::receive(ptr.into(), controller).await
}
use surrealism_types::{Args, args::Args};
use surrealdb_types::SurrealValue;
// Define function with typed arguments
fn my_function<T, U>(args: (T, U)) -> Result<()>
where
T: SurrealValue,
U: SurrealValue,
{
// Convert args to SurrealDB Values
let values = args.to_values();
// ... use values ...
Ok(())
}
// Reconstruct typed arguments from Values
let args: (String, i64) = Args::from_values(values)?;
To implement this protocol in another language:
Implement Memory Management
Implement Wire Format
Implement Type Marshalling
Test Cross-Language Compatibility
The protocol includes several safety mechanisms:
free() prevents leaksPtr wrapper prevents raw pointer confusionbytes::Bytes enables efficient memory sharingWhen adding new Serializable implementations:
See the main SurrealDB repository for license information.