dpp_plugin_sdk/abi.rs
1//! Low-level linear-memory ABI: allocation, deallocation, and buffer packing
2//! across the Wasm host/guest boundary.
3
4use std::alloc::{Layout, alloc as mem_alloc, dealloc as mem_dealloc};
5
6/// Allocate `len` bytes in the module's linear memory and return the
7/// pointer as a `u32`. Returns `0` for a zero-length request.
8#[must_use]
9pub fn host_alloc(len: u32) -> u32 {
10 if len == 0 {
11 return 0;
12 }
13 let layout = Layout::from_size_align(len as usize, 1).expect("valid layout");
14 // SAFETY: `layout` has non-zero size; a null return is handled by the host.
15 unsafe { mem_alloc(layout) as u32 }
16}
17
18/// Free a buffer previously returned by [`host_alloc`] (or packed into a
19/// `-> u64` ABI return). No-op for null pointers or zero length.
20pub fn host_dealloc(ptr: u32, len: u32) {
21 if ptr == 0 || len == 0 {
22 return;
23 }
24 let layout = Layout::from_size_align(len as usize, 1).expect("valid layout");
25 // SAFETY: `ptr`/`len` must describe a buffer from `host_alloc`.
26 unsafe { mem_dealloc(ptr as *mut u8, layout) }
27}
28
29/// View the host-written input buffer as a byte slice.
30///
31/// # Safety
32///
33/// `ptr` and `len` must describe a single allocation written by the host
34/// (via `alloc`) that lives for the duration of the returned borrow.
35#[must_use]
36pub unsafe fn read_input<'a>(ptr: u32, len: u32) -> &'a [u8] {
37 unsafe {
38 if len == 0 {
39 return &[];
40 }
41 std::slice::from_raw_parts(ptr as *const u8, len as usize)
42 }
43}
44
45/// Leak `bytes` into linear memory and return the packed
46/// `(ptr << 32) | len` the host uses to read and later free it.
47///
48/// The buffer is shrunk to an exact-size allocation (`capacity == len`,
49/// align 1) so that the host's `dealloc(ptr, len)` frees precisely the
50/// allocation it was given. Returning a `Vec` directly would leak its
51/// (possibly larger) capacity and make `dealloc` a size-mismatched free.
52#[must_use]
53pub fn write_output(bytes: Vec<u8>) -> u64 {
54 let mut boxed = bytes.into_boxed_slice();
55 let out_len = boxed.len() as u32;
56 let out_ptr = boxed.as_mut_ptr() as usize as u32;
57 std::mem::forget(boxed);
58 ((out_ptr as u64) << 32) | (out_len as u64)
59}