scotch-host 0.1.0

Scotch host api for creating WASM plugins.
Documentation
use crate::{PrefixType, ScotchHostError};
use bincode::{config::standard, Decode, Encode};
use std::{marker::PhantomData, mem::size_of};
use wasmer::{
    AsStoreMut, FromToNativeWasmType, Instance, Memory32, MemorySize, MemoryView,
    NativeWasmTypeInto,
};

#[doc(hidden)]
pub struct ManagedPtr<T: Encode + Decode, M: MemorySize = Memory32> {
    offset: M::Offset,
    _ty: PhantomData<T>,
}

impl<T: Encode + Decode, M: MemorySize> ManagedPtr<T, M> {
    pub(crate) fn new(offset: M::Offset) -> Self {
        Self {
            offset,
            _ty: PhantomData,
        }
    }

    pub fn read(&self, view: &MemoryView) -> Result<(T, usize), ScotchHostError> {
        let offset: u64 = self.offset.into();
        let mut buf = [0; size_of::<PrefixType>()];
        view.read(offset, &mut buf)?;

        let len = PrefixType::from_le_bytes(buf) as usize;
        if len < 256 {
            let mut buf = [0; 256];
            view.read(offset + size_of::<PrefixType>() as u64, &mut buf[..len])?;
            Ok((bincode::decode_from_slice(&buf[..len], standard())?.0, len))
        } else {
            let mut buf = Vec::with_capacity(len);
            unsafe {
                buf.set_len(len);
            }

            view.read(offset + size_of::<PrefixType>() as u64, &mut buf[..])?;
            Ok((
                bincode::decode_from_slice(&buf[..], standard())?.0,
                buf.len(),
            ))
        }
    }

    pub fn free_in(
        &self,
        len: usize,
        store: &mut impl AsStoreMut,
        instance: &Instance,
    ) -> Result<(), ScotchHostError> {
        let func = instance
            .exports
            .get_function("__scotch_free")
            .map_err(ScotchHostError::FreeMissing)?;
        func.call(
            store,
            &[
                (self.offset.into() as i32).into(),
                ((len + size_of::<PrefixType>()) as i32).into(),
                1i32.into(),
            ],
        )
        .map(|_| ())
        .map_err(ScotchHostError::FreeFailed)
    }
}

unsafe impl<T: Encode + Decode, M: MemorySize> FromToNativeWasmType for ManagedPtr<T, M>
where
    M::Native: NativeWasmTypeInto,
{
    type Native = M::Native;

    #[inline]
    fn from_native(native: Self::Native) -> Self {
        Self {
            offset: M::native_to_offset(native),
            _ty: PhantomData,
        }
    }

    #[inline]
    fn to_native(self) -> Self::Native {
        M::offset_to_native(self.offset)
    }
}