use crate::{
util::{pack_ptr_and_len, unpack_ptr_and_len},
RIType,
};
#[cfg(not(substrate_runtime))]
use crate::host::*;
#[cfg(substrate_runtime)]
use crate::wasm::*;
#[cfg(not(substrate_runtime))]
use sp_wasm_interface::{FunctionContext, Pointer, Result};
#[cfg(not(substrate_runtime))]
use alloc::{format, string::String};
use alloc::vec::Vec;
use core::{any::type_name, marker::PhantomData};
pub struct PassPointerAndReadCopy<T, const N: usize>(PhantomData<(T, [u8; N])>);
impl<T, const N: usize> RIType for PassPointerAndReadCopy<T, N> {
type FFIType = u32;
type Inner = T;
}
#[cfg(not(substrate_runtime))]
impl<'a, T, const N: usize> FromFFIValue<'a> for PassPointerAndReadCopy<T, N>
where
T: From<[u8; N]> + Copy,
{
type Owned = T;
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<Self::Owned> {
let mut out = [0; N];
context.read_memory_into(Pointer::new(arg), &mut out)?;
Ok(T::from(out))
}
#[inline]
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
*owned
}
}
#[cfg(substrate_runtime)]
impl<T, const N: usize> IntoFFIValue for PassPointerAndReadCopy<T, N>
where
T: AsRef<[u8]>,
{
type Destructor = ();
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
assert_eq!(value.as_ref().len(), N);
(value.as_ref().as_ptr() as u32, ())
}
}
pub struct PassPointerAndRead<T, const N: usize>(PhantomData<(T, [u8; N])>);
impl<'a, T, const N: usize> RIType for PassPointerAndRead<&'a T, N> {
type FFIType = u32;
type Inner = &'a T;
}
#[cfg(not(substrate_runtime))]
impl<'a, T, const N: usize> FromFFIValue<'a> for PassPointerAndRead<&'a T, N>
where
T: From<[u8; N]>,
{
type Owned = T;
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<Self::Owned> {
let mut out = [0; N];
context.read_memory_into(Pointer::new(arg), &mut out)?;
Ok(T::from(out))
}
#[inline]
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
&*owned
}
}
#[cfg(substrate_runtime)]
impl<'a, T, const N: usize> IntoFFIValue for PassPointerAndRead<&'a T, N>
where
T: AsRef<[u8]>,
{
type Destructor = ();
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
assert_eq!(value.as_ref().len(), N);
(value.as_ref().as_ptr() as u32, ())
}
}
pub struct PassFatPointerAndRead<T>(PhantomData<T>);
impl<T> RIType for PassFatPointerAndRead<T> {
type FFIType = u64;
type Inner = T;
}
#[cfg(not(substrate_runtime))]
impl<'a> FromFFIValue<'a> for PassFatPointerAndRead<&'a [u8]> {
type Owned = Vec<u8>;
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<Self::Owned> {
let (ptr, len) = unpack_ptr_and_len(arg);
context.read_memory(Pointer::new(ptr), len)
}
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
&*owned
}
}
#[cfg(not(substrate_runtime))]
impl<'a> FromFFIValue<'a> for PassFatPointerAndRead<&'a str> {
type Owned = String;
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<Self::Owned> {
let (ptr, len) = unpack_ptr_and_len(arg);
let vec = context.read_memory(Pointer::new(ptr), len)?;
String::from_utf8(vec).map_err(|_| "could not parse '&str' when marshalling hostcall's arguments through the FFI boundary: the string is not valid UTF-8".into())
}
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
&*owned
}
}
#[cfg(not(substrate_runtime))]
impl<'a> FromFFIValue<'a> for PassFatPointerAndRead<Vec<u8>> {
type Owned = Vec<u8>;
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<Self::Owned> {
<PassFatPointerAndRead<&[u8]> as FromFFIValue>::from_ffi_value(context, arg)
}
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
core::mem::take(owned)
}
}
#[cfg(substrate_runtime)]
impl<T> IntoFFIValue for PassFatPointerAndRead<T>
where
T: AsRef<[u8]>,
{
type Destructor = ();
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
let value = value.as_ref();
(pack_ptr_and_len(value.as_ptr() as u32, value.len() as u32), ())
}
}
pub struct PassFatPointerAndReadWrite<T>(PhantomData<T>);
impl<T> RIType for PassFatPointerAndReadWrite<T> {
type FFIType = u64;
type Inner = T;
}
#[cfg(not(substrate_runtime))]
impl<'a> FromFFIValue<'a> for PassFatPointerAndReadWrite<&'a mut [u8]> {
type Owned = Vec<u8>;
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<Self::Owned> {
let (ptr, len) = unpack_ptr_and_len(arg);
context.read_memory(Pointer::new(ptr), len)
}
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
&mut *owned
}
fn write_back_into_runtime(
value: Self::Owned,
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<()> {
let (ptr, len) = unpack_ptr_and_len(arg);
assert_eq!(len as usize, value.len());
context.write_memory(Pointer::new(ptr), &value)
}
}
#[cfg(substrate_runtime)]
impl<'a> IntoFFIValue for PassFatPointerAndReadWrite<&'a mut [u8]> {
type Destructor = ();
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
(pack_ptr_and_len(value.as_ptr() as u32, value.len() as u32), ())
}
}
pub struct PassPointerAndWrite<T, const N: usize>(PhantomData<(T, [u8; N])>);
impl<T, const N: usize> RIType for PassPointerAndWrite<T, N> {
type FFIType = u32;
type Inner = T;
}
#[cfg(not(substrate_runtime))]
impl<'a, T, const N: usize> FromFFIValue<'a> for PassPointerAndWrite<&'a mut T, N>
where
T: Default + AsRef<[u8]>,
{
type Owned = T;
fn from_ffi_value(
_context: &mut dyn FunctionContext,
_arg: Self::FFIType,
) -> Result<Self::Owned> {
Ok(T::default())
}
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
&mut *owned
}
fn write_back_into_runtime(
value: Self::Owned,
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<()> {
let value = value.as_ref();
assert_eq!(value.len(), N);
context.write_memory(Pointer::new(arg), value)
}
}
#[cfg(substrate_runtime)]
impl<'a, T, const N: usize> IntoFFIValue for PassPointerAndWrite<&'a mut T, N>
where
T: AsMut<[u8]>,
{
type Destructor = ();
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
let value = value.as_mut();
assert_eq!(value.len(), N);
(value.as_ptr() as u32, ())
}
}
pub struct PassFatPointerAndDecode<T>(PhantomData<T>);
impl<T> RIType for PassFatPointerAndDecode<T> {
type FFIType = u64;
type Inner = T;
}
#[cfg(not(substrate_runtime))]
impl<'a, T: codec::Decode> FromFFIValue<'a> for PassFatPointerAndDecode<T> {
type Owned = Option<T>;
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<Self::Owned> {
let (ptr, len) = unpack_ptr_and_len(arg);
let vec = context.read_memory(Pointer::new(ptr), len)?;
T::decode(&mut &vec[..]).map_err(|error| format!(
"could not SCALE-decode '{}' when marshalling hostcall's arguments through the FFI boundary: {error}",
type_name::<T>())
).map(Some)
}
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
owned.take().expect("this is called only once and is never 'None'")
}
}
#[cfg(substrate_runtime)]
impl<T: codec::Encode> IntoFFIValue for PassFatPointerAndDecode<T> {
type Destructor = Vec<u8>;
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
let data = value.encode();
(pack_ptr_and_len(data.as_ptr() as u32, data.len() as u32), data)
}
}
pub struct PassFatPointerAndDecodeSlice<T>(PhantomData<T>);
impl<T> RIType for PassFatPointerAndDecodeSlice<T> {
type FFIType = u64;
type Inner = T;
}
#[cfg(not(substrate_runtime))]
impl<'a, T: codec::Decode> FromFFIValue<'a> for PassFatPointerAndDecodeSlice<&'a [T]> {
type Owned = Vec<T>;
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<Self::Owned> {
let (ptr, len) = unpack_ptr_and_len(arg);
let vec = context.read_memory(Pointer::new(ptr), len)?;
<Vec::<T> as codec::Decode>::decode(&mut &vec[..]).map_err(|error| format!(
"could not SCALE-decode '{}' when marshalling hostcall's arguments through the FFI boundary: {error}",
type_name::<Vec<T>>()
))
}
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
&*owned
}
}
#[cfg(substrate_runtime)]
impl<'a, T: codec::Encode> IntoFFIValue for PassFatPointerAndDecodeSlice<&'a [T]> {
type Destructor = Vec<u8>;
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
let data = codec::Encode::encode(value);
(pack_ptr_and_len(data.as_ptr() as u32, data.len() as u32), data)
}
}
trait Primitive: Copy {}
impl Primitive for u8 {}
impl Primitive for u16 {}
impl Primitive for u32 {}
impl Primitive for u64 {}
impl Primitive for i8 {}
impl Primitive for i16 {}
impl Primitive for i32 {}
impl Primitive for i64 {}
pub struct PassAs<T, U>(PhantomData<(T, U)>);
impl<T, U> RIType for PassAs<T, U>
where
U: RIType,
{
type FFIType = <U as RIType>::FFIType;
type Inner = T;
}
#[cfg(not(substrate_runtime))]
impl<'a, T, U> FromFFIValue<'a> for PassAs<T, U>
where
U: RIType + FromFFIValue<'a> + Primitive,
T: TryFrom<<U as FromFFIValue<'a>>::Owned> + Copy,
{
type Owned = T;
fn from_ffi_value(
context: &mut dyn FunctionContext,
arg: Self::FFIType,
) -> Result<Self::Owned> {
<U as FromFFIValue>::from_ffi_value(context, arg).and_then(|value| value.try_into()
.map_err(|_| format!(
"failed to convert '{}' (passed as '{}') into '{}' when marshalling hostcall's arguments through the FFI boundary",
type_name::<T>(),
type_name::<Self::FFIType>(),
type_name::<Self::Owned>()
)))
}
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
*owned
}
}
#[cfg(substrate_runtime)]
impl<T, U> IntoFFIValue for PassAs<T, U>
where
U: RIType + IntoFFIValue + Primitive,
U::Inner: From<T>,
T: Copy,
{
type Destructor = <U as IntoFFIValue>::Destructor;
fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
let mut value = U::Inner::from(*value);
<U as IntoFFIValue>::into_ffi_value(&mut value)
}
}
pub struct ReturnAs<T, U>(PhantomData<(T, U)>);
impl<T, U> RIType for ReturnAs<T, U>
where
U: RIType,
{
type FFIType = <U as RIType>::FFIType;
type Inner = T;
}
#[cfg(not(substrate_runtime))]
impl<T, U> IntoFFIValue for ReturnAs<T, U>
where
U: RIType + IntoFFIValue + Primitive,
<U as RIType>::Inner: From<Self::Inner>,
{
fn into_ffi_value(
value: Self::Inner,
context: &mut dyn FunctionContext,
) -> Result<Self::FFIType> {
let value: <U as RIType>::Inner = value.into();
<U as IntoFFIValue>::into_ffi_value(value, context)
}
}
#[cfg(substrate_runtime)]
impl<T, U> FromFFIValue for ReturnAs<T, U>
where
U: RIType + FromFFIValue + Primitive,
Self::Inner: TryFrom<U::Inner>,
{
fn from_ffi_value(arg: Self::FFIType) -> Self::Inner {
let value = <U as FromFFIValue>::from_ffi_value(arg);
match Self::Inner::try_from(value) {
Ok(value) => value,
Err(_) => {
panic!(
"failed to convert '{}' (passed as '{}') into a '{}' when marshalling a hostcall's return value through the FFI boundary",
type_name::<U::Inner>(),
type_name::<Self::FFIType>(),
type_name::<Self::Inner>()
);
},
}
}
}
pub struct AllocateAndReturnPointer<T, const N: usize>(PhantomData<(T, [u8; N])>);
impl<T, const N: usize> RIType for AllocateAndReturnPointer<T, N> {
type FFIType = u32;
type Inner = T;
}
#[cfg(not(substrate_runtime))]
impl<T, const N: usize> IntoFFIValue for AllocateAndReturnPointer<T, N>
where
T: AsRef<[u8]>,
{
fn into_ffi_value(
value: Self::Inner,
context: &mut dyn FunctionContext,
) -> Result<Self::FFIType> {
let value = value.as_ref();
assert_eq!(
value.len(),
N,
"expected the byte blob to be {N} bytes long, is {} bytes when returning '{}' from a host function",
value.len(),
type_name::<T>()
);
let addr = context.allocate_memory(value.len() as u32)?;
context.write_memory(addr, value)?;
Ok(addr.into())
}
}
#[cfg(substrate_runtime)]
impl<T: codec::Decode, const N: usize> FromFFIValue for AllocateAndReturnPointer<T, N>
where
T: From<[u8; N]>,
{
fn from_ffi_value(arg: Self::FFIType) -> Self::Inner {
let value = unsafe { Vec::from_raw_parts(arg as *mut u8, N, N) };
let array = unsafe { *(value.as_ptr() as *const [u8; N]) };
T::from(array)
}
}
pub struct AllocateAndReturnFatPointer<T>(PhantomData<T>);
impl<T> RIType for AllocateAndReturnFatPointer<T> {
type FFIType = u64;
type Inner = T;
}
#[cfg(not(substrate_runtime))]
impl<T> IntoFFIValue for AllocateAndReturnFatPointer<T>
where
T: AsRef<[u8]>,
{
fn into_ffi_value(
value: Self::Inner,
context: &mut dyn FunctionContext,
) -> Result<Self::FFIType> {
let value = value.as_ref();
let ptr = context.allocate_memory(value.len() as u32)?;
context.write_memory(ptr, &value)?;
Ok(pack_ptr_and_len(ptr.into(), value.len() as u32))
}
}
#[cfg(substrate_runtime)]
impl<T> FromFFIValue for AllocateAndReturnFatPointer<T>
where
T: From<Vec<u8>>,
{
fn from_ffi_value(arg: Self::FFIType) -> Self::Inner {
let (ptr, len) = unpack_ptr_and_len(arg);
let len = len as usize;
let vec = if len == 0 {
Vec::new()
} else {
unsafe { Vec::from_raw_parts(ptr as *mut u8, len, len) }
};
T::from(vec)
}
}
pub struct AllocateAndReturnByCodec<T>(PhantomData<T>);
impl<T> RIType for AllocateAndReturnByCodec<T> {
type FFIType = u64;
type Inner = T;
}
#[cfg(not(substrate_runtime))]
impl<T: codec::Encode> IntoFFIValue for AllocateAndReturnByCodec<T> {
fn into_ffi_value(value: T, context: &mut dyn FunctionContext) -> Result<Self::FFIType> {
let vec = value.encode();
let ptr = context.allocate_memory(vec.len() as u32)?;
context.write_memory(ptr, &vec)?;
Ok(pack_ptr_and_len(ptr.into(), vec.len() as u32))
}
}
#[cfg(substrate_runtime)]
impl<T: codec::Decode> FromFFIValue for AllocateAndReturnByCodec<T> {
fn from_ffi_value(arg: Self::FFIType) -> Self::Inner {
let (ptr, len) = unpack_ptr_and_len(arg);
let len = len as usize;
let encoded = if len == 0 {
bytes::Bytes::new()
} else {
bytes::Bytes::from(unsafe { Vec::from_raw_parts(ptr as *mut u8, len, len) })
};
match codec::decode_from_bytes(encoded) {
Ok(value) => value,
Err(error) => {
panic!(
"failed to decode '{}' when marshalling a hostcall's return value through the FFI boundary: {error}",
type_name::<T>(),
);
},
}
}
}