b6d35f6faf
Updated 4763 files with dual copyright: - Parity Technologies (UK) Ltd. - Dijital Kurdistan Tech Institute
713 lines
21 KiB
Rust
713 lines
21 KiB
Rust
// This file is part of Bizinikiwi.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! Provides host <-> runtime FFI marshalling strategy newtype wrappers
|
|
//! for defining runtime interfaces.
|
|
|
|
use crate::{
|
|
util::{pack_ptr_and_len, unpack_ptr_and_len},
|
|
RIType,
|
|
};
|
|
|
|
#[cfg(not(bizinikiwi_runtime))]
|
|
use crate::host::*;
|
|
|
|
#[cfg(bizinikiwi_runtime)]
|
|
use crate::wasm::*;
|
|
|
|
#[cfg(not(bizinikiwi_runtime))]
|
|
use pezsp_wasm_interface::{FunctionContext, Pointer, Result};
|
|
|
|
#[cfg(not(bizinikiwi_runtime))]
|
|
use alloc::{format, string::String};
|
|
|
|
use alloc::vec::Vec;
|
|
use core::{any::type_name, marker::PhantomData};
|
|
|
|
/// Pass a value into the host by a thin pointer.
|
|
///
|
|
/// This casts the value into a `&[u8]` using `AsRef<[u8]>` and passes a pointer to that byte blob
|
|
/// to the host. Then the host reads `N` bytes from that address into an `[u8; N]`, converts it
|
|
/// into target type using `From<[u8; N]>` and passes it into the host function by a copy.
|
|
///
|
|
/// Use [`PassPointerAndRead`] if you want to have the host function accept a reference type
|
|
/// on the host side or if you'd like to avoid the extra copy.
|
|
///
|
|
/// Raw FFI type: `u32` (a pointer)
|
|
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(bizinikiwi_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(bizinikiwi_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) {
|
|
// Using an 'assert' instead of a 'T: AsRef<[u8; N]>` bound since a '[u8; N]' *doesn't*
|
|
// implement it.
|
|
assert_eq!(value.as_ref().len(), N);
|
|
(value.as_ref().as_ptr() as u32, ())
|
|
}
|
|
}
|
|
|
|
/// Pass a value into the host by a thin pointer.
|
|
///
|
|
/// This casts the value into a `&[u8]` using `AsRef<[u8]>` and passes a pointer to that byte blob
|
|
/// to the host. Then the host reads `N` bytes from that address into an `[u8; N]`, converts it
|
|
/// into target type using `From<[u8; N]>` and passes it into the host function by a reference.
|
|
///
|
|
/// This can only be used with reference types (e.g. `&[u8; 32]`). Use [`PassPointerAndReadCopy`]
|
|
/// if you want to have the host function accept a non-reference type on the host side.
|
|
///
|
|
/// Raw FFI type: `u32` (a pointer)
|
|
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(bizinikiwi_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(bizinikiwi_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, ())
|
|
}
|
|
}
|
|
|
|
/// Pass a value into the host by a fat pointer.
|
|
///
|
|
/// This casts the value into a `&[u8]` and passes a pointer to that byte blob and its length
|
|
/// to the host. Then the host reads that blob and converts it into an owned type and passes it
|
|
/// (either as an owned type or as a reference) to the host function.
|
|
///
|
|
/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
|
|
pub struct PassFatPointerAndRead<T>(PhantomData<T>);
|
|
|
|
impl<T> RIType for PassFatPointerAndRead<T> {
|
|
type FFIType = u64;
|
|
type Inner = T;
|
|
}
|
|
|
|
#[cfg(not(bizinikiwi_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(bizinikiwi_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(bizinikiwi_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(bizinikiwi_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), ())
|
|
}
|
|
}
|
|
|
|
/// Pass a value into the host by a fat pointer, writing it back after the host call ends.
|
|
///
|
|
/// This casts the value into a `&mut [u8]` and passes a pointer to that byte blob and its length
|
|
/// to the host. Then the host reads that blob and converts it into an owned type and passes it
|
|
/// as a mutable reference to the host function. After the host function finishes the byte blob
|
|
/// is written back into the guest memory.
|
|
///
|
|
/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
|
|
pub struct PassFatPointerAndReadWrite<T>(PhantomData<T>);
|
|
|
|
impl<T> RIType for PassFatPointerAndReadWrite<T> {
|
|
type FFIType = u64;
|
|
type Inner = T;
|
|
}
|
|
|
|
#[cfg(not(bizinikiwi_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(bizinikiwi_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), ())
|
|
}
|
|
}
|
|
|
|
/// Pass a pointer into the host and write to it after the host call ends.
|
|
///
|
|
/// This casts a given type into `&mut [u8]` using `AsMut<[u8]>` and passes a pointer to
|
|
/// that byte slice into the host. The host *doesn't* read from this and instead creates
|
|
/// a default instance of type `T` and passes it as a `&mut T` into the host function
|
|
/// implementation. After the host function finishes this value is then cast into a `&[u8]` using
|
|
/// `AsRef<[u8]>` and written back into the guest memory.
|
|
///
|
|
/// Raw FFI type: `u32` (a pointer)
|
|
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(bizinikiwi_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(bizinikiwi_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, ())
|
|
}
|
|
}
|
|
|
|
/// Pass a `T` into the host using the SCALE codec.
|
|
///
|
|
/// This encodes a `T` into a `Vec<u8>` using the SCALE codec and then
|
|
/// passes a pointer to that byte blob and its length to the host,
|
|
/// which then reads it and decodes back into `T`.
|
|
///
|
|
/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
|
|
pub struct PassFatPointerAndDecode<T>(PhantomData<T>);
|
|
|
|
impl<T> RIType for PassFatPointerAndDecode<T> {
|
|
type FFIType = u64;
|
|
type Inner = T;
|
|
}
|
|
|
|
#[cfg(not(bizinikiwi_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(bizinikiwi_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)
|
|
}
|
|
}
|
|
|
|
/// Pass a `&[T]` into the host using the SCALE codec.
|
|
///
|
|
/// This encodes a `&[T]` into a `Vec<u8>` using the SCALE codec and then
|
|
/// passes a pointer to that byte blob and its length to the host,
|
|
/// which then reads it and decodes back into `Vec<T>` and passes
|
|
/// a reference to that (as `&[T]`) into the host function.
|
|
///
|
|
/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
|
|
pub struct PassFatPointerAndDecodeSlice<T>(PhantomData<T>);
|
|
|
|
impl<T> RIType for PassFatPointerAndDecodeSlice<T> {
|
|
type FFIType = u64;
|
|
type Inner = T;
|
|
}
|
|
|
|
#[cfg(not(bizinikiwi_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(bizinikiwi_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)
|
|
}
|
|
}
|
|
|
|
/// A trait signifying a primitive type.
|
|
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 {}
|
|
|
|
/// Pass `T` through the FFI boundary by first converting it to `U` in the runtime, and then
|
|
/// converting it back to `T` on the host's side.
|
|
///
|
|
/// Raw FFI type: same as `U`'s FFI type
|
|
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(bizinikiwi_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::<U>(),
|
|
type_name::<Self::FFIType>(),
|
|
type_name::<Self::Owned>()
|
|
)))
|
|
}
|
|
|
|
fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
|
|
*owned
|
|
}
|
|
}
|
|
|
|
#[cfg(bizinikiwi_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)
|
|
}
|
|
}
|
|
|
|
/// Return `T` through the FFI boundary by first converting it to `U` on the host's side, and then
|
|
/// converting it back to `T` in the runtime.
|
|
///
|
|
/// Raw FFI type: same as `U`'s FFI type
|
|
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(bizinikiwi_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(bizinikiwi_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>()
|
|
);
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
/// (DEPRECATED) Return `T` as a blob of bytes into the runtime.
|
|
///
|
|
/// Uses `T::AsRef<[u8]>` to cast `T` into a `&[u8]`, allocates runtime memory
|
|
/// using the legacy allocator, copies the slice into the runtime memory, and
|
|
/// returns a pointer to it.
|
|
///
|
|
/// THIS STRATEGY IS DEPRECATED; DO NOT USE FOR NEW HOST FUNCTIONS!
|
|
///
|
|
/// Ideally use a mutable slice to return data to the guest, for example using
|
|
/// the [`PassPointerAndWrite`] strategy.
|
|
///
|
|
/// Raw FFI type: `u32` (a pointer to the byte blob)
|
|
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(bizinikiwi_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(bizinikiwi_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 {
|
|
// SAFETY: This memory was allocated by the host allocator with the exact
|
|
// capacity needed, so it's safe to make a `Vec` out of it.
|
|
let value = unsafe { Vec::from_raw_parts(arg as *mut u8, N, N) };
|
|
|
|
// SAFETY: Reading a `[u8; N]` from a `&[u8]` which is at least `N` elements long is safe.
|
|
let array = unsafe { *(value.as_ptr() as *const [u8; N]) };
|
|
T::from(array)
|
|
}
|
|
}
|
|
|
|
/// (DEPRECATED) Return `T` as a blob of bytes into the runtime.
|
|
///
|
|
/// Uses `T::AsRef<[u8]>` to cast `T` into a `&[u8]`, allocates runtime memory
|
|
/// using the legacy allocator, copies the slice into the runtime memory, and
|
|
/// returns a pointer to it.
|
|
///
|
|
/// THIS STRATEGY IS DEPRECATED; DO NOT USE FOR NEW HOST FUNCTIONS!
|
|
///
|
|
/// Ideally use a mutable slice to return data to the guest, for example using
|
|
/// the [`PassPointerAndWrite`] strategy.
|
|
///
|
|
/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
|
|
pub struct AllocateAndReturnFatPointer<T>(PhantomData<T>);
|
|
|
|
impl<T> RIType for AllocateAndReturnFatPointer<T> {
|
|
type FFIType = u64;
|
|
type Inner = T;
|
|
}
|
|
|
|
#[cfg(not(bizinikiwi_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(bizinikiwi_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 {
|
|
// SAFETY: This memory was allocated by the host allocator with the exact
|
|
// capacity needed, so it's safe to make a `Vec` out of it.
|
|
unsafe { Vec::from_raw_parts(ptr as *mut u8, len, len) }
|
|
};
|
|
|
|
T::from(vec)
|
|
}
|
|
}
|
|
|
|
/// (DEPRECATED) Return `T` into the runtime using the SCALE codec.
|
|
///
|
|
/// Encodes `T` using the SCALE codec, allocates runtime memory using the legacy
|
|
/// allocator, copies the encoded payload into the runtime memory, and returns
|
|
/// a fat pointer to it.
|
|
///
|
|
/// THIS STRATEGY IS DEPRECATED; DO NOT USE FOR NEW HOST FUNCTIONS!
|
|
///
|
|
/// Ideally use a mutable slice to return data to the guest, for example using
|
|
/// the [`PassPointerAndWrite`] strategy.
|
|
///
|
|
/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
|
|
pub struct AllocateAndReturnByCodec<T>(PhantomData<T>);
|
|
|
|
impl<T> RIType for AllocateAndReturnByCodec<T> {
|
|
type FFIType = u64;
|
|
type Inner = T;
|
|
}
|
|
|
|
#[cfg(not(bizinikiwi_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(bizinikiwi_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 {
|
|
// SAFETY: This memory was allocated by the host allocator with the exact
|
|
// capacity needed, so it's safe to make a `Vec` out of it.
|
|
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>(),
|
|
);
|
|
},
|
|
}
|
|
}
|
|
}
|