feat: Hook up float host functions

This commit is contained in:
TimothyBanks
2026-08-11 10:10:41 -04:00
parent 7d52867e3c
commit 97f32869df
11 changed files with 1558 additions and 2 deletions

View File

@@ -463,4 +463,104 @@ host_functions! {
#[gas = 60]
#[wasm_name = "nft_serial"]
fn get_nft_sequence(&self, nft_id: &[u8], out: &mut [u8]) -> HostResult<usize>;
// A "float" here is an XRPL `Number` in its serialized form: a byte blob the guest
// holds opaquely and hands back to these functions. Inputs and outputs that are
// floats are byte regions; `mode` is the rounding mode, a scalar the guest chooses.
/// A float built from the signed integer `x` under rounding `mode`. Writes the
/// float bytes; no input region.
#[gas = 100]
#[wasm_name = "float_from_int"]
fn float_from_int(&self, x: i64, mode: i32, out: &mut [u8]) -> HostResult<usize>;
/// A float built from the unsigned integer in the 8-byte region `x` under rounding
/// `mode`. Reads the integer region and writes the float bytes.
#[gas = 130]
#[wasm_name = "float_from_uint"]
fn float_from_uint(&self, x: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
/// A float built from the serialized `STAmount` in `amount` under rounding `mode`.
/// Reads the amount region and writes the float bytes.
#[gas = 150]
#[wasm_name = "float_from_stamount"]
fn float_from_stamount(&self, amount: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
/// A float built from the serialized `STNumber` in `number` under rounding `mode`.
/// Reads the number region and writes the float bytes.
#[gas = 150]
#[wasm_name = "float_from_stnumber"]
fn float_from_stnumber(&self, number: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
/// The float `x` rounded to a signed integer under rounding `mode`. Reads the float
/// region and writes the integer as its eight little-endian bytes.
#[gas = 130]
#[wasm_name = "float_to_int"]
fn float_to_int(&self, x: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
/// The float `x` split into its mantissa and exponent. Reads the float region and
/// writes the mantissa (eight little-endian bytes) and the exponent (four little-
/// endian bytes) to two separate output regions.
#[gas = 130]
#[wasm_name = "float_to_mant_exp"]
fn float_to_mant_exp(
&self,
x: &[u8],
mantissa_out: &mut [u8],
exponent_out: &mut [u8],
) -> HostResult<usize>;
/// A float built from `mantissa` and `exponent` under rounding `mode`. Writes the
/// float bytes; no input region.
#[gas = 100]
#[wasm_name = "float_from_mant_exp"]
fn float_from_mant_exp(
&self,
mantissa: i64,
exponent: i32,
mode: i32,
out: &mut [u8],
) -> HostResult<usize>;
/// Compares floats `x` and `y`, returning a negative, zero, or positive scalar as
/// `x` is less than, equal to, or greater than `y`. Reads both float regions.
#[gas = 80]
#[wasm_name = "float_cmp"]
fn float_compare(&self, x: &[u8], y: &[u8]) -> HostResult<i32>;
/// The float sum `x + y` under rounding `mode`. Reads both float regions and writes
/// the result bytes.
#[gas = 160]
#[wasm_name = "float_add"]
fn float_add(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
/// The float difference `x - y` under rounding `mode`. Reads both float regions and
/// writes the result bytes.
#[gas = 160]
#[wasm_name = "float_sub"]
fn float_subtract(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
/// The float product `x * y` under rounding `mode`. Reads both float regions and
/// writes the result bytes.
#[gas = 300]
#[wasm_name = "float_mult"]
fn float_multiply(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
/// The float quotient `x / y` under rounding `mode`. Reads both float regions and
/// writes the result bytes.
#[gas = 300]
#[wasm_name = "float_div"]
fn float_divide(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
/// The `n`-th root of the float `x` under rounding `mode`. Reads the float region
/// and writes the result bytes.
#[gas = 5500]
#[wasm_name = "float_root"]
fn float_root(&self, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> HostResult<usize>;
/// The float `x` raised to the power `n` under rounding `mode`. Reads the float
/// region and writes the result bytes.
#[gas = 5500]
#[wasm_name = "float_pow"]
fn float_power(&self, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> HostResult<usize>;
}

View File

@@ -455,6 +455,126 @@ impl HostFunctions for FakeHost {
}
put(out, &nft_id[0].to_le_bytes())
}
/// A scalar-in float: writes the low byte of `x` as a stand-in float.
fn float_from_int(&self, x: i64, _mode: i32, out: &mut [u8]) -> HostResult<usize> {
put(out, &[x as u8])
}
/// A byte-in float; `InvalidParams` on an empty region.
fn float_from_uint(&self, x: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
if x.is_empty() {
return Err(HostError::InvalidParams);
}
put(out, &[x[0]])
}
/// The same, for a serialized amount.
fn float_from_stamount(&self, amount: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
if amount.is_empty() {
return Err(HostError::InvalidParams);
}
put(out, &[amount[0]])
}
/// The same, for a serialized number.
fn float_from_stnumber(&self, number: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
if number.is_empty() {
return Err(HostError::InvalidParams);
}
put(out, &[number[0]])
}
/// A float rounded to an integer, written as bytes.
fn float_to_int(&self, x: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
if x.is_empty() {
return Err(HostError::InvalidParams);
}
put(out, &[x[0]])
}
/// Writes a mantissa (its first byte) and an exponent (its first byte) to two
/// regions, returning their combined length.
fn float_to_mant_exp(
&self,
x: &[u8],
mantissa_out: &mut [u8],
exponent_out: &mut [u8],
) -> HostResult<usize> {
if x.is_empty() {
return Err(HostError::InvalidParams);
}
let m = put(mantissa_out, &[x[0]])?;
let e = put(exponent_out, &[x[0]])?;
Ok(m + e)
}
/// A two-scalar-in float.
fn float_from_mant_exp(
&self,
mantissa: i64,
_exponent: i32,
_mode: i32,
out: &mut [u8],
) -> HostResult<usize> {
put(out, &[mantissa as u8])
}
/// Reads two floats and returns a scalar; `InvalidParams` if either is empty.
fn float_compare(&self, x: &[u8], y: &[u8]) -> HostResult<i32> {
if x.is_empty() || y.is_empty() {
return Err(HostError::InvalidParams);
}
Ok(i32::from(x[0]) - i32::from(y[0]))
}
/// A binary float operator; `InvalidParams` if either operand is empty.
fn float_add(&self, x: &[u8], y: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
if x.is_empty() || y.is_empty() {
return Err(HostError::InvalidParams);
}
put(out, &[x[0]])
}
/// The same shape, for subtraction.
fn float_subtract(&self, x: &[u8], y: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
if x.is_empty() || y.is_empty() {
return Err(HostError::InvalidParams);
}
put(out, &[x[0]])
}
/// The same shape, for multiplication.
fn float_multiply(&self, x: &[u8], y: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
if x.is_empty() || y.is_empty() {
return Err(HostError::InvalidParams);
}
put(out, &[x[0]])
}
/// The same shape, for division.
fn float_divide(&self, x: &[u8], y: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
if x.is_empty() || y.is_empty() {
return Err(HostError::InvalidParams);
}
put(out, &[x[0]])
}
/// A one-float-and-integer operator; `InvalidParams` on an empty operand.
fn float_root(&self, x: &[u8], _n: i32, _mode: i32, out: &mut [u8]) -> HostResult<usize> {
if x.is_empty() {
return Err(HostError::InvalidParams);
}
put(out, &[x[0]])
}
/// The same shape, for exponentiation.
fn float_power(&self, x: &[u8], _n: i32, _mode: i32, out: &mut [u8]) -> HostResult<usize> {
if x.is_empty() {
return Err(HostError::InvalidParams);
}
put(out, &[x[0]])
}
}
#[test]
@@ -687,6 +807,26 @@ fn the_trait_is_implementable() {
assert_eq!(host.get_nft_flags(&[]), Err(HostError::InvalidParams));
assert_eq!(host.get_nft_transfer_fee(&[9; 32]), Ok(9));
assert_eq!(host.get_nft_sequence(&[9; 32], &mut out), Ok(1));
assert_eq!(host.float_from_int(5, 0, &mut out), Ok(1));
assert_eq!(host.float_from_uint(&[3; 8], 0, &mut out), Ok(1));
assert_eq!(host.float_from_stamount(&[3; 8], 0, &mut out), Ok(1));
assert_eq!(host.float_from_stnumber(&[3; 8], 0, &mut out), Ok(1));
assert_eq!(host.float_to_int(&[3; 8], 0, &mut out), Ok(1));
let mut mant = [0u8; 8];
let mut exp = [0u8; 4];
assert_eq!(host.float_to_mant_exp(&[3; 8], &mut mant, &mut exp), Ok(2));
assert_eq!(host.float_from_mant_exp(5, 0, 0, &mut out), Ok(1));
assert_eq!(host.float_compare(&[9; 8], &[4; 8]), Ok(5));
assert_eq!(
host.float_compare(&[], &[4; 8]),
Err(HostError::InvalidParams)
);
assert_eq!(host.float_add(&[3; 8], &[4; 8], 0, &mut out), Ok(1));
assert_eq!(host.float_subtract(&[3; 8], &[4; 8], 0, &mut out), Ok(1));
assert_eq!(host.float_multiply(&[3; 8], &[4; 8], 0, &mut out), Ok(1));
assert_eq!(host.float_divide(&[3; 8], &[4; 8], 0, &mut out), Ok(1));
assert_eq!(host.float_root(&[3; 8], 2, 0, &mut out), Ok(1));
assert_eq!(host.float_power(&[3; 8], 2, 0, &mut out), Ok(1));
assert_eq!(*host.traced.borrow(), ["hello/2/true", "count=-1"]);
}
@@ -798,6 +938,20 @@ fn the_spec_table_matches_the_declarations() {
("nft_flags", 60),
("nft_xfer_fee", 60),
("nft_serial", 60),
("float_from_int", 100),
("float_from_uint", 130),
("float_from_stamount", 150),
("float_from_stnumber", 150),
("float_to_int", 130),
("float_to_mant_exp", 130),
("float_from_mant_exp", 100),
("float_cmp", 80),
("float_add", 160),
("float_sub", 160),
("float_mult", 300),
("float_div", 300),
("float_root", 5500),
("float_pow", 5500),
]
);
}

View File

@@ -419,6 +419,77 @@ mod ffi {
#[namespace = "xrpl"]
#[cxx_name = "getNFTSequence"]
fn get_nft_sequence(self: &HostContext, nft_id: &[u8], out: &mut [u8]) -> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatFromInt"]
fn float_from_int(self: &HostContext, x: i64, mode: i32, out: &mut [u8]) -> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatFromUint"]
fn float_from_uint(self: &HostContext, x: &[u8], mode: i32, out: &mut [u8]) -> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatFromSTAmount"]
fn float_from_stamount(self: &HostContext, amount: &[u8], mode: i32, out: &mut [u8])
-> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatFromSTNumber"]
fn float_from_stnumber(self: &HostContext, number: &[u8], mode: i32, out: &mut [u8])
-> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatToInt"]
fn float_to_int(self: &HostContext, x: &[u8], mode: i32, out: &mut [u8]) -> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatToMantExp"]
fn float_to_mant_exp(
self: &HostContext,
x: &[u8],
mantissa_out: &mut [u8],
exponent_out: &mut [u8],
) -> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatFromMantExp"]
fn float_from_mant_exp(
self: &HostContext,
mantissa: i64,
exponent: i32,
mode: i32,
out: &mut [u8],
) -> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatCompare"]
fn float_compare(self: &HostContext, x: &[u8], y: &[u8]) -> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatAdd"]
fn float_add(self: &HostContext, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatSubtract"]
fn float_subtract(self: &HostContext, x: &[u8], y: &[u8], mode: i32, out: &mut [u8])
-> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatMultiply"]
fn float_multiply(self: &HostContext, x: &[u8], y: &[u8], mode: i32, out: &mut [u8])
-> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatDivide"]
fn float_divide(self: &HostContext, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatRoot"]
fn float_root(self: &HostContext, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> i32;
#[namespace = "xrpl"]
#[cxx_name = "floatPower"]
fn float_power(self: &HostContext, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> i32;
}
}
@@ -713,6 +784,73 @@ impl HostFunctions for CxxHost<'_> {
fn get_nft_sequence(&self, nft_id: &[u8], out: &mut [u8]) -> HostResult<usize> {
bytes_written(self.ctx.get_nft_sequence(nft_id, out))
}
fn float_from_int(&self, x: i64, mode: i32, out: &mut [u8]) -> HostResult<usize> {
bytes_written(self.ctx.float_from_int(x, mode, out))
}
fn float_from_uint(&self, x: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
bytes_written(self.ctx.float_from_uint(x, mode, out))
}
fn float_from_stamount(&self, amount: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
bytes_written(self.ctx.float_from_stamount(amount, mode, out))
}
fn float_from_stnumber(&self, number: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
bytes_written(self.ctx.float_from_stnumber(number, mode, out))
}
fn float_to_int(&self, x: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
bytes_written(self.ctx.float_to_int(x, mode, out))
}
fn float_to_mant_exp(
&self,
x: &[u8],
mantissa_out: &mut [u8],
exponent_out: &mut [u8],
) -> HostResult<usize> {
bytes_written(self.ctx.float_to_mant_exp(x, mantissa_out, exponent_out))
}
fn float_from_mant_exp(
&self,
mantissa: i64,
exponent: i32,
mode: i32,
out: &mut [u8],
) -> HostResult<usize> {
bytes_written(self.ctx.float_from_mant_exp(mantissa, exponent, mode, out))
}
fn float_compare(&self, x: &[u8], y: &[u8]) -> HostResult<i32> {
scalar(self.ctx.float_compare(x, y))
}
fn float_add(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
bytes_written(self.ctx.float_add(x, y, mode, out))
}
fn float_subtract(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
bytes_written(self.ctx.float_subtract(x, y, mode, out))
}
fn float_multiply(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
bytes_written(self.ctx.float_multiply(x, y, mode, out))
}
fn float_divide(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
bytes_written(self.ctx.float_divide(x, y, mode, out))
}
fn float_root(&self, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> HostResult<usize> {
bytes_written(self.ctx.float_root(x, n, mode, out))
}
fn float_power(&self, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> HostResult<usize> {
bytes_written(self.ctx.float_power(x, n, mode, out))
}
}
fn run_escrow(

View File

@@ -174,6 +174,69 @@ pub(crate) fn write_buffered(
Ok(n)
}
/// The mantissa and exponent widths `float_to_mant_exp` writes: an `i64` and an `i32`.
/// Fixed by the ABI, not the guest, so the split is a constant rather than a reported
/// length.
const MANTISSA_BYTES: usize = 8;
const EXPONENT_BYTES: usize = 4;
/// Service `float_to_mant_exp`, the one call that writes two output regions: the host
/// fills the run's output buffer with the mantissa followed by the exponent, and each
/// is copied to its own guest region once every rule has passed.
///
/// Like [`write_buffered`], the host reads its input from the guest's memory and writes
/// to a scratch buffer, so the input stays borrowed rather than copied. The two output
/// regions are judged after the input, and the mantissa's region before the exponent's,
/// so the first fault reported is the leftmost.
pub(crate) fn write_mant_exp(
caller: &mut Caller<'_, VmState<'_>>,
mantissa_out: Region,
exponent_out: Region,
call: impl FnOnce(&dyn HostFunctions, &[u8], &mut [u8], &mut [u8]) -> HostResult<usize>,
) -> HostResult<i32> {
let mem = memory(caller)?;
let (data, state) = mem.data_and_store_mut(&mut *caller);
let host: &dyn HostFunctions = state.host;
// The scratch buffer is split at the fixed mantissa width: the host fills the first
// eight bytes with the mantissa and the next four with the exponent.
let (mant_buf, exp_buf) = state.out_buffer.split_at_mut(MANTISSA_BYTES);
let mant_buf = &mut mant_buf[..MANTISSA_BYTES];
let exp_buf = &mut exp_buf[..EXPONENT_BYTES];
let total = call(host, data, mant_buf, exp_buf)?;
// Copy the mantissa, then the exponent, each only if its whole value fits its
// region — a region too small is `BufferTooSmall`, with nothing written.
let mant_range = mantissa_out.range()?;
let mant_dst = data
.get_mut(mant_range)
.ok_or(HostError::PointerOutOfBounds)?;
if mant_dst.len() < MANTISSA_BYTES {
return Err(HostError::BufferTooSmall);
}
mant_dst[..MANTISSA_BYTES].copy_from_slice(&state.out_buffer[..MANTISSA_BYTES]);
let exp_range = exponent_out.range()?;
let exp_dst = data
.get_mut(exp_range)
.ok_or(HostError::PointerOutOfBounds)?;
if exp_dst.len() < EXPONENT_BYTES {
return Err(HostError::BufferTooSmall);
}
exp_dst[..EXPONENT_BYTES]
.copy_from_slice(&state.out_buffer[MANTISSA_BYTES..MANTISSA_BYTES + EXPONENT_BYTES]);
charge_transfer(state, MANTISSA_BYTES + EXPONENT_BYTES)?;
#[expect(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
reason = "the total is 12, far inside i32"
)]
let total = total as i32;
Ok(total)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -405,6 +468,99 @@ mod tests {
fn get_nft_sequence(&self, _nft_id: &[u8], _out: &mut [u8]) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
fn float_from_int(&self, _x: i64, _mode: i32, _out: &mut [u8]) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
fn float_from_uint(&self, _x: &[u8], _mode: i32, _out: &mut [u8]) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
fn float_from_stamount(
&self,
_amount: &[u8],
_mode: i32,
_out: &mut [u8],
) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
fn float_from_stnumber(
&self,
_number: &[u8],
_mode: i32,
_out: &mut [u8],
) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
fn float_to_int(&self, _x: &[u8], _mode: i32, _out: &mut [u8]) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
fn float_to_mant_exp(
&self,
_x: &[u8],
_mantissa_out: &mut [u8],
_exponent_out: &mut [u8],
) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
fn float_from_mant_exp(
&self,
_mantissa: i64,
_exponent: i32,
_mode: i32,
_out: &mut [u8],
) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
fn float_compare(&self, _x: &[u8], _y: &[u8]) -> HostResult<i32> {
unreachable!("no unit test in this module calls the host")
}
fn float_add(
&self,
_x: &[u8],
_y: &[u8],
_mode: i32,
_out: &mut [u8],
) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
fn float_subtract(
&self,
_x: &[u8],
_y: &[u8],
_mode: i32,
_out: &mut [u8],
) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
fn float_multiply(
&self,
_x: &[u8],
_y: &[u8],
_mode: i32,
_out: &mut [u8],
) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
fn float_divide(
&self,
_x: &[u8],
_y: &[u8],
_mode: i32,
_out: &mut [u8],
) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
fn float_root(&self, _x: &[u8], _n: i32, _mode: i32, _out: &mut [u8]) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
fn float_power(
&self,
_x: &[u8],
_n: i32,
_mode: i32,
_out: &mut [u8],
) -> HostResult<usize> {
unreachable!("no unit test in this module calls the host")
}
}
fn state(budget: u64) -> VmState<'static> {

View File

@@ -1,4 +1,4 @@
use crate::abi::{charged, read_borrowed, write_buffered, write_into};
use crate::abi::{charged, read_borrowed, write_buffered, write_into, write_mant_exp};
use crate::region::Region;
use crate::vm::VmState;
use wasmi::{Caller, Linker};
@@ -898,6 +898,281 @@ pub(crate) fn register_host_functions(
})
},
),
HostFunctionSpec::FloatFromInt => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
x: i64,
out_ptr: i32,
out_len: i32,
mode: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatFromInt, |c| {
let out = Region::new(out_ptr, out_len);
write_into(c, out, |host, out| host.float_from_int(x, mode, out))
})
},
),
HostFunctionSpec::FloatFromUint => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
in_ptr: i32,
in_len: i32,
out_ptr: i32,
out_len: i32,
mode: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatFromUint, |c| {
let out = Region::new(out_ptr, out_len);
let x = Region::new(in_ptr, in_len);
write_buffered(c, out, |host, data, buf| {
host.float_from_uint(x.read(data)?, mode, buf)
})
})
},
),
HostFunctionSpec::FloatFromStamount => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
in_ptr: i32,
in_len: i32,
out_ptr: i32,
out_len: i32,
mode: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatFromStamount, |c| {
let out = Region::new(out_ptr, out_len);
let amount = Region::new(in_ptr, in_len);
write_buffered(c, out, |host, data, buf| {
host.float_from_stamount(amount.read(data)?, mode, buf)
})
})
},
),
HostFunctionSpec::FloatFromStnumber => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
in_ptr: i32,
in_len: i32,
out_ptr: i32,
out_len: i32,
mode: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatFromStnumber, |c| {
let out = Region::new(out_ptr, out_len);
let number = Region::new(in_ptr, in_len);
write_buffered(c, out, |host, data, buf| {
host.float_from_stnumber(number.read(data)?, mode, buf)
})
})
},
),
HostFunctionSpec::FloatToInt => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
in_ptr: i32,
in_len: i32,
out_ptr: i32,
out_len: i32,
mode: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatToInt, |c| {
let out = Region::new(out_ptr, out_len);
let x = Region::new(in_ptr, in_len);
write_buffered(c, out, |host, data, buf| {
host.float_to_int(x.read(data)?, mode, buf)
})
})
},
),
HostFunctionSpec::FloatToMantExp => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
in_ptr: i32,
in_len: i32,
mant_ptr: i32,
mant_len: i32,
exp_ptr: i32,
exp_len: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatToMantExp, |c| {
let mantissa = Region::new(mant_ptr, mant_len);
let exponent = Region::new(exp_ptr, exp_len);
let x = Region::new(in_ptr, in_len);
write_mant_exp(c, mantissa, exponent, |host, data, mant, exp| {
host.float_to_mant_exp(x.read(data)?, mant, exp)
})
})
},
),
HostFunctionSpec::FloatFromMantExp => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
mantissa: i64,
exponent: i32,
out_ptr: i32,
out_len: i32,
mode: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatFromMantExp, |c| {
let out = Region::new(out_ptr, out_len);
write_into(c, out, |host, out| {
host.float_from_mant_exp(mantissa, exponent, mode, out)
})
})
},
),
HostFunctionSpec::FloatCompare => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
x_ptr: i32,
x_len: i32,
y_ptr: i32,
y_len: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatCompare, |c| {
let host = c.data().host;
let x = read_borrowed(c, Region::new(x_ptr, x_len))?;
let y = read_borrowed(c, Region::new(y_ptr, y_len))?;
host.float_compare(x, y)
})
},
),
HostFunctionSpec::FloatAdd => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
x_ptr: i32,
x_len: i32,
y_ptr: i32,
y_len: i32,
out_ptr: i32,
out_len: i32,
mode: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatAdd, |c| {
let out = Region::new(out_ptr, out_len);
let x = Region::new(x_ptr, x_len);
let y = Region::new(y_ptr, y_len);
write_buffered(c, out, |host, data, buf| {
host.float_add(x.read(data)?, y.read(data)?, mode, buf)
})
})
},
),
HostFunctionSpec::FloatSubtract => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
x_ptr: i32,
x_len: i32,
y_ptr: i32,
y_len: i32,
out_ptr: i32,
out_len: i32,
mode: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatSubtract, |c| {
let out = Region::new(out_ptr, out_len);
let x = Region::new(x_ptr, x_len);
let y = Region::new(y_ptr, y_len);
write_buffered(c, out, |host, data, buf| {
host.float_subtract(x.read(data)?, y.read(data)?, mode, buf)
})
})
},
),
HostFunctionSpec::FloatMultiply => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
x_ptr: i32,
x_len: i32,
y_ptr: i32,
y_len: i32,
out_ptr: i32,
out_len: i32,
mode: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatMultiply, |c| {
let out = Region::new(out_ptr, out_len);
let x = Region::new(x_ptr, x_len);
let y = Region::new(y_ptr, y_len);
write_buffered(c, out, |host, data, buf| {
host.float_multiply(x.read(data)?, y.read(data)?, mode, buf)
})
})
},
),
HostFunctionSpec::FloatDivide => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
x_ptr: i32,
x_len: i32,
y_ptr: i32,
y_len: i32,
out_ptr: i32,
out_len: i32,
mode: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatDivide, |c| {
let out = Region::new(out_ptr, out_len);
let x = Region::new(x_ptr, x_len);
let y = Region::new(y_ptr, y_len);
write_buffered(c, out, |host, data, buf| {
host.float_divide(x.read(data)?, y.read(data)?, mode, buf)
})
})
},
),
HostFunctionSpec::FloatRoot => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
in_ptr: i32,
in_len: i32,
n: i32,
out_ptr: i32,
out_len: i32,
mode: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatRoot, |c| {
let out = Region::new(out_ptr, out_len);
let x = Region::new(in_ptr, in_len);
write_buffered(c, out, |host, data, buf| {
host.float_root(x.read(data)?, n, mode, buf)
})
})
},
),
HostFunctionSpec::FloatPower => linker.func_wrap(
HOST_MODULE,
op.wasm_name(),
|mut caller: Caller<'_, VmState<'_>>,
in_ptr: i32,
in_len: i32,
n: i32,
out_ptr: i32,
out_len: i32,
mode: i32|
-> Result<i32, wasmi::Error> {
charged(&mut caller, HostFunctionSpec::FloatPower, |c| {
let out = Region::new(out_ptr, out_len);
let x = Region::new(in_ptr, in_len);
write_buffered(c, out, |host, data, buf| {
host.float_power(x.read(data)?, n, mode, buf)
})
})
},
),
}?;
}
Ok(())

View File

@@ -289,6 +289,76 @@ fn call_for(op: HostFunctionSpec) -> Call {
"(call $nft_serial (i32.const 0) (i32.const 32) (i32.const 32) (i32.const 4))",
4,
),
HostFunctionSpec::FloatFromInt => (
import::FLOAT_FROM_INT,
"(call $float_from_int (i64.const 0) (i32.const 0) (i32.const 8) (i32.const 0))",
4,
),
HostFunctionSpec::FloatFromUint => (
import::FLOAT_FROM_UINT,
"(call $float_from_uint (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 0))",
5,
),
HostFunctionSpec::FloatFromStamount => (
import::FLOAT_FROM_STAMOUNT,
"(call $float_from_stamount (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 0))",
5,
),
HostFunctionSpec::FloatFromStnumber => (
import::FLOAT_FROM_STNUMBER,
"(call $float_from_stnumber (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 0))",
5,
),
HostFunctionSpec::FloatToInt => (
import::FLOAT_TO_INT,
"(call $float_to_int (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 0))",
5,
),
HostFunctionSpec::FloatToMantExp => (
import::FLOAT_TO_MANT_EXP,
"(call $float_to_mant_exp (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 16) (i32.const 4))",
6,
),
HostFunctionSpec::FloatFromMantExp => (
import::FLOAT_FROM_MANT_EXP,
"(call $float_from_mant_exp (i64.const 0) (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 0))",
5,
),
HostFunctionSpec::FloatCompare => (
import::FLOAT_CMP,
"(call $float_cmp (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8))",
4,
),
HostFunctionSpec::FloatAdd => (
import::FLOAT_ADD,
"(call $float_add (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 16) (i32.const 8) (i32.const 0))",
7,
),
HostFunctionSpec::FloatSubtract => (
import::FLOAT_SUB,
"(call $float_sub (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 16) (i32.const 8) (i32.const 0))",
7,
),
HostFunctionSpec::FloatMultiply => (
import::FLOAT_MULT,
"(call $float_mult (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 16) (i32.const 8) (i32.const 0))",
7,
),
HostFunctionSpec::FloatDivide => (
import::FLOAT_DIV,
"(call $float_div (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 16) (i32.const 8) (i32.const 0))",
7,
),
HostFunctionSpec::FloatRoot => (
import::FLOAT_ROOT,
"(call $float_root (i32.const 0) (i32.const 8) (i32.const 2) (i32.const 8) (i32.const 8) (i32.const 0))",
6,
),
HostFunctionSpec::FloatPower => (
import::FLOAT_POW,
"(call $float_pow (i32.const 0) (i32.const 8) (i32.const 2) (i32.const 8) (i32.const 8) (i32.const 0))",
6,
),
};
Call {
import,

View File

@@ -866,6 +866,112 @@ fn nft_serial_reads_the_id_and_writes_four_bytes() {
assert_eq!(*host.nft_sequences_asked.borrow(), vec![nft_id]);
}
/// A float built from an i64 scalar and no input region: the value and mode reach the
/// host, and the float bytes it answers land where the guest asked. `float_from_int`
/// carries a genuine `i64` parameter, so this pins that the wide scalar survives.
#[test]
fn float_from_int_passes_the_value_and_writes_the_float() {
let host = FakeHost::new().answering_float(support::Answer::filler(8));
let wat = module(
&[import::FLOAT_FROM_INT, ONE_PAGE],
"(call $float_from_int (i64.const 42) (i32.const 64) (i32.const 8) (i32.const 3))",
);
assert_eq!(status(&wat, &host), 8, "the float length");
assert_eq!(*host.float_from_int_asked.borrow(), vec![(42, 3)]);
}
/// A float built from an 8-byte input region: the integer bytes and mode reach the
/// host, and the float bytes it answers land where the guest asked.
#[test]
fn float_from_uint_reads_the_input_and_writes_the_float() {
let host = FakeHost::new().answering_float(support::Answer::filler(8));
let wat = module(
&[import::FLOAT_FROM_UINT, ONE_PAGE],
"(call $float_from_uint (i32.const 0) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 1))",
);
assert_eq!(status(&wat, &host), 8, "the float length");
assert_eq!(
*host.float_from_uint_asked.borrow(),
vec![(vec![0u8; 8], 1)]
);
}
/// The one call that writes two output regions: the mantissa lands in the first, the
/// exponent in the second, and the status is their combined length.
#[test]
fn float_to_mant_exp_writes_both_regions() {
let host =
FakeHost::new().answering_float_mant_exp(vec![1, 2, 3, 4, 5, 6, 7, 8], vec![9, 10, 11, 12]);
// Mantissa to offset 64, exponent to offset 80; read the first byte of each back.
let wat = module(
&[import::FLOAT_TO_MANT_EXP, ONE_PAGE],
"(call $float_to_mant_exp (i32.const 0) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 80) (i32.const 4))",
);
assert_eq!(status(&wat, &host), 12, "the mantissa and exponent lengths");
assert_eq!(*host.float_to_mant_exp_asked.borrow(), vec![vec![0u8; 8]]);
let wat = module(
&[import::FLOAT_TO_MANT_EXP, ONE_PAGE],
"(drop (call $float_to_mant_exp (i32.const 0) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 80) (i32.const 4)))
(i32.load8_u (i32.const 80))",
);
assert_eq!(status(&wat, &host), 9, "the exponent's first byte");
}
/// A comparison that reads two float regions and returns a scalar verdict, no output
/// region involved.
#[test]
fn float_cmp_reads_both_and_returns_the_verdict() {
let host = FakeHost::new().answering_float_compare(Ok(-1));
let wat = module(
&[import::FLOAT_CMP, ONE_PAGE],
"(call $float_cmp (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8))",
);
assert_eq!(status(&wat, &host), -1, "the comparison verdict");
assert_eq!(
*host.float_compare_asked.borrow(),
vec![(vec![0u8; 8], vec![0u8; 8])]
);
}
/// A binary operator that reads two float regions and a mode, and writes the result:
/// both operands and the mode reach the host, tagged by operator.
#[test]
fn float_add_reads_both_operands_and_the_mode() {
let host = FakeHost::new().answering_float(support::Answer::filler(8));
let wat = module(
&[import::FLOAT_ADD, ONE_PAGE],
"(call $float_add (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 2))",
);
assert_eq!(status(&wat, &host), 8, "the result length");
assert_eq!(
*host.float_binops_asked.borrow(),
vec![("add", vec![0u8; 8], vec![0u8; 8], 2)]
);
}
/// A unary operator that reads one float region, an integer, and a mode: all three
/// reach the host, tagged by operator.
#[test]
fn float_root_reads_the_float_the_degree_and_the_mode() {
let host = FakeHost::new().answering_float(support::Answer::filler(8));
let wat = module(
&[import::FLOAT_ROOT, ONE_PAGE],
"(call $float_root (i32.const 0) (i32.const 8) (i32.const 3) (i32.const 64) (i32.const 8) (i32.const 1))",
);
assert_eq!(status(&wat, &host), 8, "the result length");
assert_eq!(
*host.float_unops_asked.borrow(),
vec![("root", vec![0u8; 8], 3, 1)]
);
}
/// A leading scalar parameter reaches the host as declared.
#[test]
fn home_le_field_passes_the_field_selector_through() {

View File

@@ -98,7 +98,7 @@ fn a_disabled_feature_does_not_pass() {
/// Every host function the ABI declares, spelled as a guest imports it. The count
/// is asserted against the ABI so a function added to it cannot be left out here.
const ALL_IMPORTS: [&str; 48] = [
const ALL_IMPORTS: [&str; 62] = [
import::LDGR_INDEX,
import::PARENT_LDGR_TIME,
import::PARENT_LDGR_HASH,
@@ -147,6 +147,20 @@ const ALL_IMPORTS: [&str; 48] = [
import::NFT_FLAGS,
import::NFT_XFER_FEE,
import::NFT_SERIAL,
import::FLOAT_FROM_INT,
import::FLOAT_FROM_UINT,
import::FLOAT_FROM_STAMOUNT,
import::FLOAT_FROM_STNUMBER,
import::FLOAT_TO_INT,
import::FLOAT_TO_MANT_EXP,
import::FLOAT_FROM_MANT_EXP,
import::FLOAT_CMP,
import::FLOAT_ADD,
import::FLOAT_SUB,
import::FLOAT_MULT,
import::FLOAT_DIV,
import::FLOAT_ROOT,
import::FLOAT_POW,
];
#[test]

View File

@@ -312,6 +312,35 @@ pub struct FakeHost {
pub nft_sequences: HashMap<Vec<u8>, Answer>,
/// Every nft id `get_nft_sequence` was asked for.
pub nft_sequences_asked: RefCell<Vec<Vec<u8>>>,
/// What every float-producing call writes.
pub float_answer: Answer,
/// Every `(x, mode)` `float_from_int` was asked for.
pub float_from_int_asked: RefCell<Vec<(i64, i32)>>,
/// Every `(x, mode)` `float_from_uint` was asked for.
pub float_from_uint_asked: RefCell<Vec<(Vec<u8>, i32)>>,
/// Every `(amount, mode)` `float_from_stamount` was asked for.
pub float_from_stamount_asked: RefCell<Vec<(Vec<u8>, i32)>>,
/// Every `(number, mode)` `float_from_stnumber` was asked for.
pub float_from_stnumber_asked: RefCell<Vec<(Vec<u8>, i32)>>,
/// Every `(x, mode)` `float_to_int` was asked for.
pub float_to_int_asked: RefCell<Vec<(Vec<u8>, i32)>>,
/// The mantissa and exponent bytes `float_to_mant_exp` writes to its two regions.
pub float_mant_exp_answer: (Vec<u8>, Vec<u8>),
/// Every float `float_to_mant_exp` was asked for.
pub float_to_mant_exp_asked: RefCell<Vec<Vec<u8>>>,
/// Every `(mantissa, exponent, mode)` `float_from_mant_exp` was asked for.
pub float_from_mant_exp_asked: RefCell<Vec<(i64, i32, i32)>>,
/// What `float_compare` answers, whatever floats it is given.
pub float_compare_answer: HostResult<i32>,
/// Every `(x, y)` `float_compare` was asked for.
pub float_compare_asked: RefCell<Vec<(Vec<u8>, Vec<u8>)>>,
/// Every `(x, y, mode)` the four binary float operators were asked for, tagged by
/// operator name.
pub float_binops_asked: RefCell<Vec<(&'static str, Vec<u8>, Vec<u8>, i32)>>,
/// Every `(x, n, mode)` `float_root` and `float_power` were asked for, tagged by
/// operator name.
pub float_unops_asked: RefCell<Vec<(&'static str, Vec<u8>, i32, i32)>>,
}
impl Default for FakeHost {
@@ -414,6 +443,19 @@ impl Default for FakeHost {
nft_fee_asked: RefCell::new(Vec::new()),
nft_sequences: HashMap::new(),
nft_sequences_asked: RefCell::new(Vec::new()),
float_answer: Answer::filler(8),
float_from_int_asked: RefCell::new(Vec::new()),
float_from_uint_asked: RefCell::new(Vec::new()),
float_from_stamount_asked: RefCell::new(Vec::new()),
float_from_stnumber_asked: RefCell::new(Vec::new()),
float_to_int_asked: RefCell::new(Vec::new()),
float_mant_exp_answer: (vec![0u8; 8], vec![0u8; 4]),
float_to_mant_exp_asked: RefCell::new(Vec::new()),
float_from_mant_exp_asked: RefCell::new(Vec::new()),
float_compare_answer: Ok(0),
float_compare_asked: RefCell::new(Vec::new()),
float_binops_asked: RefCell::new(Vec::new()),
float_unops_asked: RefCell::new(Vec::new()),
}
}
}
@@ -755,6 +797,21 @@ impl FakeHost {
self
}
pub fn answering_float(mut self, answer: Answer) -> FakeHost {
self.float_answer = answer;
self
}
pub fn answering_float_mant_exp(mut self, mantissa: Vec<u8>, exponent: Vec<u8>) -> FakeHost {
self.float_mant_exp_answer = (mantissa, exponent);
self
}
pub fn answering_float_compare(mut self, answer: HostResult<i32>) -> FakeHost {
self.float_compare_answer = answer;
self
}
pub fn traces(&self) -> Vec<Trace> {
self.traces.borrow().clone()
}
@@ -1201,6 +1258,114 @@ impl HostFunctions for FakeHost {
None => Err(HostError::InvalidParams),
}
}
fn float_from_int(&self, x: i64, mode: i32, out: &mut [u8]) -> HostResult<usize> {
self.float_from_int_asked.borrow_mut().push((x, mode));
self.float_answer.fill(out)
}
fn float_from_uint(&self, x: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
self.float_from_uint_asked
.borrow_mut()
.push((x.to_vec(), mode));
self.float_answer.fill(out)
}
fn float_from_stamount(&self, amount: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
self.float_from_stamount_asked
.borrow_mut()
.push((amount.to_vec(), mode));
self.float_answer.fill(out)
}
fn float_from_stnumber(&self, number: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
self.float_from_stnumber_asked
.borrow_mut()
.push((number.to_vec(), mode));
self.float_answer.fill(out)
}
fn float_to_int(&self, x: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
self.float_to_int_asked
.borrow_mut()
.push((x.to_vec(), mode));
self.float_answer.fill(out)
}
fn float_to_mant_exp(
&self,
x: &[u8],
mantissa_out: &mut [u8],
exponent_out: &mut [u8],
) -> HostResult<usize> {
self.float_to_mant_exp_asked.borrow_mut().push(x.to_vec());
let (mantissa, exponent) = &self.float_mant_exp_answer;
mantissa_out[..mantissa.len()].copy_from_slice(mantissa);
exponent_out[..exponent.len()].copy_from_slice(exponent);
Ok(mantissa.len() + exponent.len())
}
fn float_from_mant_exp(
&self,
mantissa: i64,
exponent: i32,
mode: i32,
out: &mut [u8],
) -> HostResult<usize> {
self.float_from_mant_exp_asked
.borrow_mut()
.push((mantissa, exponent, mode));
self.float_answer.fill(out)
}
fn float_compare(&self, x: &[u8], y: &[u8]) -> HostResult<i32> {
self.float_compare_asked
.borrow_mut()
.push((x.to_vec(), y.to_vec()));
self.float_compare_answer
}
fn float_add(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
self.float_binops_asked
.borrow_mut()
.push(("add", x.to_vec(), y.to_vec(), mode));
self.float_answer.fill(out)
}
fn float_subtract(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
self.float_binops_asked
.borrow_mut()
.push(("sub", x.to_vec(), y.to_vec(), mode));
self.float_answer.fill(out)
}
fn float_multiply(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
self.float_binops_asked
.borrow_mut()
.push(("mult", x.to_vec(), y.to_vec(), mode));
self.float_answer.fill(out)
}
fn float_divide(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
self.float_binops_asked
.borrow_mut()
.push(("div", x.to_vec(), y.to_vec(), mode));
self.float_answer.fill(out)
}
fn float_root(&self, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> HostResult<usize> {
self.float_unops_asked
.borrow_mut()
.push(("root", x.to_vec(), n, mode));
self.float_answer.fill(out)
}
fn float_power(&self, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> HostResult<usize> {
self.float_unops_asked
.borrow_mut()
.push(("pow", x.to_vec(), n, mode));
self.float_answer.fill(out)
}
}
// ---------------------------------------------------------------------------
@@ -1275,6 +1440,21 @@ pub mod import {
pub const NFT_XFER_FEE: &str =
r#"(import "host_lib" "nft_xfer_fee" (func $nft_xfer_fee (param i32 i32) (result i32)))"#;
pub const NFT_SERIAL: &str = r#"(import "host_lib" "nft_serial" (func $nft_serial (param i32 i32 i32 i32) (result i32)))"#;
pub const FLOAT_FROM_INT: &str = r#"(import "host_lib" "float_from_int" (func $float_from_int (param i64 i32 i32 i32) (result i32)))"#;
pub const FLOAT_FROM_UINT: &str = r#"(import "host_lib" "float_from_uint" (func $float_from_uint (param i32 i32 i32 i32 i32) (result i32)))"#;
pub const FLOAT_FROM_STAMOUNT: &str = r#"(import "host_lib" "float_from_stamount" (func $float_from_stamount (param i32 i32 i32 i32 i32) (result i32)))"#;
pub const FLOAT_FROM_STNUMBER: &str = r#"(import "host_lib" "float_from_stnumber" (func $float_from_stnumber (param i32 i32 i32 i32 i32) (result i32)))"#;
pub const FLOAT_TO_INT: &str = r#"(import "host_lib" "float_to_int" (func $float_to_int (param i32 i32 i32 i32 i32) (result i32)))"#;
pub const FLOAT_TO_MANT_EXP: &str = r#"(import "host_lib" "float_to_mant_exp" (func $float_to_mant_exp (param i32 i32 i32 i32 i32 i32) (result i32)))"#;
pub const FLOAT_FROM_MANT_EXP: &str = r#"(import "host_lib" "float_from_mant_exp" (func $float_from_mant_exp (param i64 i32 i32 i32 i32) (result i32)))"#;
pub const FLOAT_CMP: &str =
r#"(import "host_lib" "float_cmp" (func $float_cmp (param i32 i32 i32 i32) (result i32)))"#;
pub const FLOAT_ADD: &str = r#"(import "host_lib" "float_add" (func $float_add (param i32 i32 i32 i32 i32 i32 i32) (result i32)))"#;
pub const FLOAT_SUB: &str = r#"(import "host_lib" "float_sub" (func $float_sub (param i32 i32 i32 i32 i32 i32 i32) (result i32)))"#;
pub const FLOAT_MULT: &str = r#"(import "host_lib" "float_mult" (func $float_mult (param i32 i32 i32 i32 i32 i32 i32) (result i32)))"#;
pub const FLOAT_DIV: &str = r#"(import "host_lib" "float_div" (func $float_div (param i32 i32 i32 i32 i32 i32 i32) (result i32)))"#;
pub const FLOAT_ROOT: &str = r#"(import "host_lib" "float_root" (func $float_root (param i32 i32 i32 i32 i32 i32) (result i32)))"#;
pub const FLOAT_POW: &str = r#"(import "host_lib" "float_pow" (func $float_pow (param i32 i32 i32 i32 i32 i32) (result i32)))"#;
}
/// One page of linear memory, exported under the name the engine looks for.