use std::convert::TryInto;
use oasis_runtime_sdk::context::Context;
use super::OasisV1;
use crate::{abi::ExecutionContext, Config};
pub const EXPORT_ALLOCATE: &str = "allocate";
pub const EXPORT_DEALLOCATE: &str = "deallocate";
#[derive(Debug)]
pub struct Region {
pub offset: usize,
pub length: usize,
}
#[derive(Debug, thiserror::Error)]
pub enum RegionError {
#[error("region too big")]
RegionTooBig,
#[error("bad allocation function: {0}")]
BadAllocationFunction(#[source] anyhow::Error),
#[error("region allocation failed: {0}")]
AllocationFailed(#[source] anyhow::Error),
#[error("region size mismatch")]
SizeMismatch,
#[error("bad region pointer")]
BadPointer,
}
impl Region {
pub fn to_arg(&self) -> (u32, u32) {
(self.offset as u32, self.length as u32)
}
pub fn from_arg(arg: (u32, u32)) -> Self {
Region {
offset: arg.0 as usize,
length: arg.1 as usize,
}
}
pub fn deref(memory: &wasm3::Memory<'_>, arg: u32) -> Result<Self, RegionError> {
let arg = arg as usize;
if arg + 8 > memory.size() {
return Err(RegionError::BadPointer);
}
let dst = memory.as_slice();
let offset = u32::from_le_bytes(dst[arg..arg + 4].try_into().unwrap()) as usize;
let length = u32::from_le_bytes(dst[arg + 4..arg + 8].try_into().unwrap()) as usize;
if offset + length > memory.size() {
return Err(RegionError::BadPointer);
}
Ok(Region { offset, length })
}
pub fn copy_from_slice(
&self,
memory: &mut wasm3::Memory<'_>,
src: &[u8],
) -> Result<(), RegionError> {
if src.len() != self.length {
return Err(RegionError::SizeMismatch);
}
if (self.offset + self.length) > memory.size() {
return Err(RegionError::BadPointer);
}
let dst = &mut memory.as_slice_mut()[self.offset..self.offset + self.length];
dst.copy_from_slice(src);
Ok(())
}
pub fn as_slice<'mem>(
&self,
memory: &'mem wasm3::Memory<'_>,
) -> Result<&'mem [u8], RegionError> {
if (self.offset + self.length) > memory.size() {
return Err(RegionError::BadPointer);
}
Ok(&memory.as_slice()[self.offset..self.offset + self.length])
}
pub fn as_slice_mut<'mem>(
&self,
memory: &'mem mut wasm3::Memory<'_>,
) -> Result<&'mem mut [u8], RegionError> {
if (self.offset + self.length) > memory.size() {
return Err(RegionError::BadPointer);
}
Ok(&mut memory.as_slice_mut()[self.offset..self.offset + self.length])
}
pub fn serialize(&self) -> [u8; 8] {
let mut data = [0u8; 8];
data[..4].copy_from_slice(&(self.offset as u32).to_le_bytes());
data[4..].copy_from_slice(&(self.length as u32).to_le_bytes());
data
}
}
impl<Cfg: Config> OasisV1<Cfg> {
pub fn allocate<C: Context>(
instance: &wasm3::Instance<'_, '_, ExecutionContext<'_, C>>,
length: usize,
) -> Result<Region, RegionError> {
let length: u32 = length.try_into().map_err(|_| RegionError::RegionTooBig)?;
let func = instance
.find_function::<u32, u32>(EXPORT_ALLOCATE)
.map_err(|err| RegionError::BadAllocationFunction(err.into()))?;
let offset = func
.call(length) .map_err(|err| RegionError::AllocationFailed(err.into()))?;
let region = Region {
offset: offset as usize,
length: length as usize,
};
instance
.runtime()
.try_with_memory(|memory| -> Result<(), RegionError> {
if (region.offset + region.length) > memory.size() {
return Err(RegionError::BadPointer);
}
Ok(())
})
.unwrap()?;
Ok(region)
}
pub fn allocate_and_copy<C: Context>(
instance: &wasm3::Instance<'_, '_, ExecutionContext<'_, C>>,
data: &[u8],
) -> Result<Region, RegionError> {
let dst = Self::allocate(instance, data.len())?;
instance
.runtime()
.try_with_memory(|mut memory| -> Result<(), RegionError> {
dst.copy_from_slice(&mut memory, data)?;
Ok(())
})
.unwrap()?;
Ok(dst)
}
pub fn serialize_and_allocate<C, T>(
instance: &wasm3::Instance<'_, '_, ExecutionContext<'_, C>>,
data: T,
) -> Result<Region, RegionError>
where
C: Context,
T: cbor::Encode,
{
let data = cbor::to_vec(data);
Self::allocate_and_copy(instance, &data)
}
pub fn serialize_and_allocate_as_ptr<C, T>(
instance: &wasm3::Instance<'_, '_, ExecutionContext<'_, C>>,
data: T,
) -> Result<u32, RegionError>
where
C: Context,
T: cbor::Encode,
{
let data = cbor::to_vec(data);
let outer = Self::allocate(instance, data.len() + 8)?;
let inner = Region {
offset: outer.offset + 8,
length: outer.length - 8,
};
instance
.runtime()
.try_with_memory(|mut memory| -> Result<(), RegionError> {
inner.copy_from_slice(&mut memory, &data)?;
let dst = &mut memory.as_slice_mut()[outer.offset..outer.offset + 8];
dst.copy_from_slice(&inner.serialize());
Ok(())
})
.unwrap()?;
Ok(outer.offset as u32)
}
pub fn allocate_region<C: Context>(
instance: &wasm3::Instance<'_, '_, ExecutionContext<'_, C>>,
region: Region,
) -> Result<u32, RegionError> {
let data = region.serialize();
let dst = Self::allocate(instance, data.len())?;
instance
.runtime()
.try_with_memory(|mut memory| -> Result<(), RegionError> {
dst.copy_from_slice(&mut memory, &data)?;
Ok(())
})
.unwrap()?;
Ok(dst.offset as u32)
}
}