Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Support u128/i128 in runtime interface
This implements support for `u128`/`i128` as parameters/return value in
runtime interfaces. As we can not pass them as identity, as for the
other primitives types, we pass them as an pointer to an `[u8; 16]` array.
  • Loading branch information
bkchr committed Jan 21, 2020
commit b58327c676f6de20d025e713799b92cc710fb50e
53 changes: 53 additions & 0 deletions primitives/runtime-interface/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,3 +471,56 @@ impl<T: sp_wasm_interface::PointerType> IntoFFIValue for Pointer<T> {
Ok(self.into())
}
}

/// Implement the traits for `u128`/`i128`
macro_rules! for_u128_i128 {
($type:ty) => {
/// `u128`/`i128` is passed as `u32`.
///
/// The `u32` is a pointer to an `[u8; 16]` array.
impl RIType for $type {
type FFIType = u32;
}

#[cfg(not(feature = "std"))]
impl IntoFFIValue for $type {
type Owned = ();

fn into_ffi_value(&self) -> WrappedFFIValue<u32> {
unsafe { (mem::transmute::<&Self, *const u8>(self) as u32).into() }
}
}

#[cfg(not(feature = "std"))]
impl FromFFIValue for $type {
fn from_ffi_value(arg: u32) -> $type {
unsafe { mem::transmute::<[u8; 16], $type>(<[u8; 16]>::from_ffi_value(arg)) }
}
}

#[cfg(feature = "std")]
impl FromFFIValue for $type {
type SelfInstance = $type;

fn from_ffi_value(context: &mut dyn FunctionContext, arg: u32) -> Result<$type> {
let data = context.read_memory(Pointer::new(arg), 16)?;
let mut res = [0u8; 16];
res.copy_from_slice(&data);
Ok(unsafe { mem::transmute::<[u8; 16], $type>(res) })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I wonder if this is safe? The alignment of u128 might be 16 and I am not sure sure if we can say anything regarding this about res.
  2. Does it assume that wasm and host have the same endianness?

I think this can be swapped with u128::from_le_bytes here instead of transmute?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. What? Why can we not say the same about res? It is as well 16 bytes. (Looking at the rust source code for from_le_bytes it does exactly the same transmute)
  2. Yes it assumes it. Please don't start again this xD As it is no primitive type, I probably also need to call to_le_bytes on the host?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But good catches! I searched yesterday evening for *_let_bytes, but failed for some reason 🤷‍♀️

}
}

#[cfg(feature = "std")]
impl IntoFFIValue for $type {
fn into_ffi_value(self, context: &mut dyn FunctionContext) -> Result<u32> {
let addr = context.allocate_memory(16)?;
let data = unsafe { mem::transmute::<$type, [u8; 16]>(self) };
context.write_memory(addr, &data)?;
Ok(addr.into())
}
}
}
}

for_u128_i128!(u128);
for_u128_i128!(i128);
3 changes: 3 additions & 0 deletions primitives/runtime-interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,12 @@
//! | `u16` | `u16` | `Identity` |
//! | `u32` | `u32` | `Identity` |
//! | `u64` | `u64` | `Identity` |
//! | `i128` | `u32` | `v.as_ptr()` (pointer to a 16 byte array) |
//! | `i8` | `i8` | `Identity` |
//! | `i16` | `i16` | `Identity` |
//! | `i32` | `i32` | `Identity` |
//! | `i64` | `i64` | `Identity` |
//! | `u128` | `u32` | `v.as_ptr()` (pointer to a 16 byte array) |
//! | `bool` | `u8` | `if v { 1 } else { 0 }` |
//! | `&str` | `u64` | <code>v.len() 32bit << 32 &#124; v.as_ptr() 32bit</code> |
//! | `&[u8]` | `u64` | <code>v.len() 32bit << 32 &#124; v.as_ptr() 32bit</code> |
Expand All @@ -93,6 +95,7 @@
//! | `&[T] where T: Encode` | `u64` | `let e = v.encode();`<br><br><code>e.len() 32bit << 32 &#124; e.as_ptr() 32bit</code> |
//! | `[u8; N]` | `u32` | `v.as_ptr()` |
//! | `*const T` | `u32` | `Identity` |
//! | `Option<T>` | `u64` | `let e = v.encode();`<br><br><code>e.len() 32bit << 32 &#124; e.as_ptr() 32bit</code> |
//! | [`T where T: PassBy<PassBy=Inner>`](pass_by::Inner) | Depends on inner | Depends on inner |
//! | [`T where T: PassBy<PassBy=Codec>`](pass_by::Codec) | `u64`| <code>v.len() 32bit << 32 &#124; v.as_ptr() 32bit</code> |
//!
Expand Down
20 changes: 20 additions & 0 deletions primitives/runtime-interface/test-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ pub trait TestApi {
fn overwrite_native_function_implementation() -> bool {
false
}

/// Gets an `u128` and returns this value
fn get_and_return_u128(val: u128) -> u128 {
val
}

/// Gets an `i128` and returns this value
fn get_and_return_i128(val: i128) -> i128 {
val
}
}

/// Two random external functions from the old runtime interface.
Expand Down Expand Up @@ -191,4 +201,14 @@ wasm_export_functions! {

assert!(test_api::overwrite_native_function_implementation());
}

fn test_u128_i128_as_parameter_and_return_value() {
for val in &[u128::max_value(), 1u128, 5000u128, u64::max_value() as u128] {
assert_eq!(*val, test_api::get_and_return_u128(*val));
}

for val in &[i128::max_value(), i128::min_value(), 1i128, 5000i128, u64::max_value() as i128] {
assert_eq!(*val, test_api::get_and_return_i128(*val));
}
}
}
5 changes: 5 additions & 0 deletions primitives/runtime-interface/test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,8 @@ fn test_invalid_utf8_data_should_return_an_error() {
fn test_overwrite_native_function_implementation() {
call_wasm_method::<HostFunctions>("test_overwrite_native_function_implementation");
}

#[test]
fn test_u128_i128_as_parameter_and_return_value() {
call_wasm_method::<HostFunctions>("test_u128_i128_as_parameter_and_return_value");
}