mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 14:20:56 +00:00
feat: Hook up float host functions
This commit is contained in:
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user