Hey, I am new to WASM and need to write my custom glue code to interface with the runtime. I am defining something like below:
use std::{mem::ManuallyDrop, ptr};
#[link(wasm_import_module = "env")]
extern "C" {
// These methods are provided by the WASM runtime
pub fn greetings_from_host() -> i32;
pub fn debug_log(log: i32);
}
pub struct DefaultMemory;
pub struct MemorySegment(ManuallyDrop<Vec<u8>>);
impl MemorySegment {
fn new(len: usize) -> Self {
MemorySegment(ManuallyDrop::new(Vec::with_capacity(len)))
}
}
impl DefaultMemory {
pub fn allocate(len: usize) -> *mut u8 {
let mut v = MemorySegment::new(len);
let v_ref = v.0.as_mut_ptr();
v_ref
}
unsafe fn dealloc(ptr: *mut u8, size: usize) {
let data = Vec::from_raw_parts(ptr, size, size);
std::mem::drop(data);
}
fn read_memory(ptr: i32) -> String {
let ptr = ptr as *mut u8;
let mut len_buffer = [0u8; 4];
unsafe {
ptr::copy_nonoverlapping(ptr, len_buffer.as_mut_ptr(), 4);
}
let len = u32::from_le_bytes(len_buffer);
let buffer = unsafe { Vec::from_raw_parts(ptr, 4 + len as usize, 4 + len as usize) };
String::from_utf8_lossy(&buffer[4..]).to_string()
}
fn length_prefixed_string(payload: String) -> Vec<u8> {
let buffer = payload.as_bytes();
let len = buffer.len() as u32;
let mut length_prefixed_buffer: Vec<u8> = Vec::new();
length_prefixed_buffer.extend_from_slice(&len.to_le_bytes());
length_prefixed_buffer.extend_from_slice(buffer);
return length_prefixed_buffer;
}
pub fn greetings_from_host() -> String {
let ptr = unsafe { greetings_from_host() };
DefaultMemory::read_memory(ptr)
}
pub fn debug_log(s: String) {
let s = DefaultMemory::length_prefixed_string(s);
unsafe { debug_log(s.as_ptr() as _) }
}
}
Basically I want to pass back and forth a length prefixed UTF-8 encoded string. My question is that is it correct to use ManuallyDrop
here and then from ptr construct the Vec
and taking ownership of that data.