#[macro_export]
macro_rules! export_package_handler {
($handler:ty) => {
#[cfg(target_arch = "wasm32")]
const _: () = {
use ::std::cell::{Cell, RefCell};
use ::std::vec::Vec;
use $crate::{
wire, BlobStore, PackageHandler, WireArtifactId, WireHttpRequest,
WireListRequest, WirePutRequest, Writer,
};
::std::thread_local! {
static ALLOCS: RefCell<Vec<Vec<u8>>> = const { RefCell::new(Vec::new()) };
static RESULT_LEN: Cell<u32> = const { Cell::new(0) };
}
#[link(wasm_import_module = "env")]
extern "C" {
fn host_store_get(key_ptr: u32, key_len: u32) -> u64;
fn host_store_put(key_ptr: u32, key_len: u32, data_ptr: u32, data_len: u32) -> u64;
fn host_store_list(prefix_ptr: u32, prefix_len: u32) -> u64;
fn host_store_size(key_ptr: u32, key_len: u32) -> u64;
}
#[no_mangle]
pub extern "C" fn alloc(size: u32) -> u32 {
ALLOCS.with(|a| {
let mut buf = ::std::vec![0u8; size as usize];
let ptr = buf.as_mut_ptr() as u32;
a.borrow_mut().push(buf);
ptr
})
}
#[no_mangle]
pub extern "C" fn result_len() -> u32 {
RESULT_LEN.with(|l| l.get())
}
fn ret(bytes: &[u8]) -> u32 {
let ptr = alloc(bytes.len() as u32);
unsafe {
::std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr as *mut u8, bytes.len());
}
RESULT_LEN.with(|l| l.set(bytes.len() as u32));
ptr
}
unsafe fn arg<'a>(ptr: u32, len: u32) -> &'a [u8] {
::std::slice::from_raw_parts(ptr as *const u8, len as usize)
}
fn unpack(packed: u64) -> Option<Vec<u8>> {
if packed == 0 {
return None;
}
let ptr = (packed >> 32) as u32;
let len = (packed & 0xffff_ffff) as usize;
Some(unsafe { ::std::slice::from_raw_parts(ptr as *const u8, len) }.to_vec())
}
struct HostStore;
impl BlobStore for HostStore {
fn get(&self, key: &str) -> Result<Option<Vec<u8>>, String> {
let packed =
unsafe { host_store_get(key.as_ptr() as u32, key.len() as u32) };
let raw = unpack(packed)
.ok_or_else(|| ::std::format!("host_store_get({key}) failed"))?;
wire::decode_store_get(&raw).map_err(|e| e.message())
}
fn put(&self, key: &str, data: &[u8]) -> Result<(), String> {
let packed = unsafe {
host_store_put(
key.as_ptr() as u32,
key.len() as u32,
data.as_ptr() as u32,
data.len() as u32,
)
};
let raw = unpack(packed)
.ok_or_else(|| ::std::format!("host_store_put({key}) failed"))?;
wire::decode_unit_response(&raw).map_err(|e| e.message())?
}
fn list(&self, prefix: &str) -> Result<Vec<(String, u64)>, String> {
let packed =
unsafe { host_store_list(prefix.as_ptr() as u32, prefix.len() as u32) };
let raw = unpack(packed)
.ok_or_else(|| ::std::format!("host_store_list({prefix}) failed"))?;
wire::decode_store_listing(&raw).map_err(|e| e.message())
}
fn size(&self, key: &str) -> Result<Option<u64>, String> {
let packed =
unsafe { host_store_size(key.as_ptr() as u32, key.len() as u32) };
let raw = unpack(packed)
.ok_or_else(|| ::std::format!("host_store_size({key}) failed"))?;
let payload = wire::decode_store_get(&raw).map_err(|e| e.message())?;
match payload {
None => Ok(None),
Some(b) if b.len() == 8 => {
let mut a = [0u8; 8];
a.copy_from_slice(&b);
Ok(Some(u64::from_le_bytes(a)))
}
Some(b) => Err(::std::format!(
"host_store_size({key}) returned {} bytes, expected 8",
b.len()
)),
}
}
}
fn handler() -> $handler {
<$handler as ::std::default::Default>::default()
}
#[no_mangle]
pub extern "C" fn plugin_manifest() -> u32 {
ret(&handler().manifest().to_bytes())
}
#[no_mangle]
pub unsafe extern "C" fn plugin_fetch(ptr: u32, len: u32) -> u32 {
let out = match WireArtifactId::from_bytes(unsafe { arg(ptr, len) }) {
Ok(id) => handler().fetch(&HostStore, &id),
Err(e) => Err(e.message()),
};
ret(&wire::encode_fetch_response(&out))
}
#[no_mangle]
pub unsafe extern "C" fn plugin_put(ptr: u32, len: u32) -> u32 {
let out = match WirePutRequest::from_bytes(unsafe { arg(ptr, len) }) {
Ok(req) => handler().put(&HostStore, &req.id, &req.data),
Err(e) => Err(e.message()),
};
ret(&wire::encode_unit_response(&out))
}
#[no_mangle]
pub unsafe extern "C" fn plugin_list(ptr: u32, len: u32) -> u32 {
let out = match WireListRequest::from_bytes(unsafe { arg(ptr, len) }) {
Ok(req) => {
handler().list(&HostStore, req.name_filter.as_deref(), req.limit as usize)
}
Err(e) => Err(e.message()),
};
ret(&wire::encode_list_response(&out))
}
#[no_mangle]
pub unsafe extern "C" fn plugin_coordinate_for_path(ptr: u32, len: u32) -> u32 {
let mut r = $crate::Reader::new(unsafe { arg(ptr, len) });
let out = match r.str("suburl").and_then(|s| r.expect_end("suburl").map(|_| s)) {
Ok(suburl) => handler().coordinate_for_path(&suburl),
Err(_) => None,
};
ret(&wire::encode_coordinate_response(&out))
}
#[no_mangle]
pub unsafe extern "C" fn plugin_http(ptr: u32, len: u32) -> u32 {
let out = match WireHttpRequest::from_bytes(unsafe { arg(ptr, len) }) {
Ok(req) => handler().http(&HostStore, &req),
Err(e) => Err(e.message()),
};
ret(&wire::encode_http_response(&out))
}
#[allow(dead_code)]
fn _writer_is_used() -> Writer {
Writer::new()
}
};
};
}