diff --git a/contracts/feature-tests/basic-features/src/managed_buffer_features.rs b/contracts/feature-tests/basic-features/src/managed_buffer_features.rs index 06c535d4a3..27b4cc4e44 100644 --- a/contracts/feature-tests/basic-features/src/managed_buffer_features.rs +++ b/contracts/feature-tests/basic-features/src/managed_buffer_features.rs @@ -7,6 +7,11 @@ pub trait ManagedBufferFeatures { ManagedBuffer::new() } + #[endpoint] + fn mbuffer_from_big_float(&self, big_float: BigFloat) -> ManagedBuffer { + ManagedBuffer::from(big_float) + } + #[endpoint] fn mbuffer_concat(&self, mb1: ManagedBuffer, mb2: ManagedBuffer) -> ManagedBuffer { let mut result = mb1; diff --git a/contracts/feature-tests/basic-features/tests/basic_features_managed_buffer_test.rs b/contracts/feature-tests/basic-features/tests/basic_features_managed_buffer_test.rs index cde4caa56e..1e3f54500a 100644 --- a/contracts/feature-tests/basic-features/tests/basic_features_managed_buffer_test.rs +++ b/contracts/feature-tests/basic-features/tests/basic_features_managed_buffer_test.rs @@ -1,4 +1,4 @@ -use multiversx_sc::types::{ManagedAddress, ManagedBuffer}; +use multiversx_sc::types::{BigFloat, BigUint, ManagedAddress, ManagedBuffer}; use multiversx_sc_scenario::{api::StaticApi, *}; use basic_features::managed_buffer_features::ManagedBufferFeatures; @@ -10,6 +10,14 @@ fn test_managed_buffer_new_empty() { assert_eq!(ManagedBuffer::new(), result); } +#[test] +fn test_managed_buffer_from_big_float() { + let bf = basic_features::contract_obj::(); + let big_float = BigFloat::from_big_uint(&BigUint::from(5u64)); + let result = bf.mbuffer_from_big_float(big_float); + println!("result is {:#?}", result); +} + #[test] fn test_managed_address_zero() { let bf = basic_features::contract_obj::(); diff --git a/framework/base/src/types/managed/basic/managed_buffer.rs b/framework/base/src/types/managed/basic/managed_buffer.rs index 7c08f32699..68d992742e 100644 --- a/framework/base/src/types/managed/basic/managed_buffer.rs +++ b/framework/base/src/types/managed/basic/managed_buffer.rs @@ -2,7 +2,7 @@ use crate::{ abi::TypeName, api::{ use_raw_handle, ErrorApiImpl, HandleConstraints, InvalidSliceError, ManagedBufferApiImpl, - ManagedTypeApi, StaticVarApiImpl, + ManagedTypeApi, ManagedTypeApiImpl, StaticVarApiImpl, }, codec::{ CodecFrom, CodecFromSelf, DecodeErrorHandler, Empty, EncodeErrorHandler, NestedDecode, @@ -15,6 +15,8 @@ use crate::{ types::{heap::BoxedBytes, ManagedType, StaticBufferRef}, }; +use super::BigFloat; + /// A byte buffer managed by an external API. #[repr(transparent)] pub struct ManagedBuffer { @@ -96,6 +98,24 @@ impl ManagedBuffer { self.overwrite(result); }); } + + pub fn mb_from_big_float(big_float: BigFloat) -> Self { + let big_float_handle = big_float.get_handle(); + let new_handle: M::ManagedBufferHandle = + use_raw_handle(M::static_var_api_impl().next_handle()); + M::managed_type_impl().mb_from_big_float(big_float_handle, new_handle.clone()); + ManagedBuffer::from_handle(new_handle) + } +} + +impl From> for ManagedBuffer +where + M: ManagedTypeApi, +{ + #[inline] + fn from(big_float: BigFloat) -> Self { + Self::mb_from_big_float(big_float) + } } impl From<&[u8]> for ManagedBuffer diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 2246fc453a..0b3891563b 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -29,6 +29,8 @@ itertools = "0.12.0" hex-literal = "=0.4.1" bitflags = "=2.4.2" colored = "2.1.0" +gob = "0.1.0" +serde = "1.0.197" [dependencies.multiversx-chain-vm-executor] version = "0.2.0" diff --git a/vm/src/vm_hooks/vh_dispatcher.rs b/vm/src/vm_hooks/vh_dispatcher.rs index 0dbfc54051..f3c2d6a79c 100644 --- a/vm/src/vm_hooks/vh_dispatcher.rs +++ b/vm/src/vm_hooks/vh_dispatcher.rs @@ -1413,7 +1413,9 @@ impl VMHooks for VMHooksDispatcher { } fn mbuffer_from_big_float(&self, m_buffer_handle: i32, big_float_handle: i32) -> i32 { - panic!("Unavailable: mbuffer_from_big_float") + self.handler + .mb_from_big_float(m_buffer_handle, big_float_handle); + 0 } fn mbuffer_storage_store(&self, key_handle: i32, source_handle: i32) -> i32 { diff --git a/vm/src/vm_hooks/vh_handler/vh_managed_types.rs b/vm/src/vm_hooks/vh_handler/vh_managed_types.rs index 45bb412d8b..92d6eea22b 100644 --- a/vm/src/vm_hooks/vh_handler/vh_managed_types.rs +++ b/vm/src/vm_hooks/vh_handler/vh_managed_types.rs @@ -3,6 +3,9 @@ mod vh_big_int; mod vh_managed_buffer; mod vh_managed_map; +use gob::{ser::TypeId, Buf, StreamDeserializer, StreamSerializer}; +use num_traits::{float, Signed, ToBytes}; +use serde::ser::Serializer; pub use vh_big_float::VMHooksBigFloat; pub use vh_big_int::VMHooksBigInt; pub use vh_managed_buffer::VMHooksManagedBuffer; @@ -14,6 +17,36 @@ use crate::types::RawHandle; use super::VMHooksError; +const FLOAT_GOB_VERSION: u8 = 1; +const GOB_DEFAULT_PREC: u64 = 53; // can also be 24 +const _W: u64 = 64; +const _S: u64 = _W / 8; +// Number of bits in a mantissa word +// 64-bit precision for IEEE-754 + +#[allow(dead_code)] +pub enum Accuracy { + Below, + Exact, + Above, +} + +pub enum Form { + Zero, + Finite, + Infinite, +} + +#[allow(dead_code)] +pub enum Mode { + ToNearestEven, // == IEEE 754-2008 roundTiesToEven // default + ToNearestAway, // == IEEE 754-2008 roundTiesToAway + ToZero, // == IEEE 754-2008 roundTowardZero + AwayFromZero, // no IEEE 754-2008 equivalent + ToNegativeInf, // == IEEE 754-2008 roundTowardNegative + ToPositiveInf, // == IEEE 754-2008 roundTowardPositive +} + /// Provides VM hook implementations for methods that deal with more than one type of managed type. /// /// It is also the trait that unifies all managed type functionality. @@ -52,4 +85,110 @@ pub trait VMHooksManagedTypes: let bytes = self.random_next_bytes(length); self.mb_set(dest_handle, bytes.as_slice()); } + + // transforms big float into bytes with respect to IEEE-754 gob encode + fn mb_from_big_float(&self, m_buffer_handle: RawHandle, big_float_handle: RawHandle) { + let big_float = self.m_types_lock().bf_get_f64(big_float_handle); + let bytes = gob_encode( + big_float, + Accuracy::Exact, + Mode::ToNearestEven, + GOB_DEFAULT_PREC, + ); + println!("encoded bytes {:#?}", bytes); + self.m_types_lock().mb_set(m_buffer_handle, bytes.to_vec()); + } + + // fn mb_from_big_float(&self, m_buffer_handle: RawHandle, big_float_handle: RawHandle) { + // let big_float = self.m_types_lock().bf_get_f64(big_float_handle); + // println!("big float is {:#?}", big_float.to_bits()); + + // let mut stream_ser = StreamSerializer::new_with_buffer(); + + // // serialize the f64 value using the obtained Serializer + // let _ = stream_ser.serialize_with_type_id(TypeId::FLOAT, &big_float); + + // // extract the serialized data + // let output_buffer = stream_ser.into_inner(); + // let bytes = output_buffer.collect(); + // println!("output buffer is {:#?}", bytes); + + // self.m_types_lock().mb_set(m_buffer_handle, bytes); + // } +} +fn gob_encode(f: f64, accuracy: Accuracy, mode: Mode, precision: u64) -> Vec { + let bits = f.to_bits(); + let (_sign, exp, mut mant) = extract_float_components(bits); + + let mut _n = 0; + let mut _form = Form::Zero; + if f.is_finite() { + let mant_number_of_bits = num_bits(mant) as u64; + _form = Form::Finite; + _n = (GOB_DEFAULT_PREC + (_W - 1)) / _W; + if mant_number_of_bits < _n { + _n = mant_number_of_bits; + } + } else { + _form = Form::Infinite; + } + + let mut buf = vec![]; + buf.push(FLOAT_GOB_VERSION); + + let mode_bits = mode as u8 & 0b111; + let acc_bits = (accuracy as u8 + 1) & 0b11; + let form_bits = _form as u8 & 0b11; + + let mut combined_byte = (mode_bits << 5) | (acc_bits << 3) | (form_bits << 1); + if f.is_sign_negative() { + combined_byte |= 1; + } + + buf.push(combined_byte); + buf.extend_from_slice(&precision.to_be_bytes()); + + if f.is_finite() { + buf.extend_from_slice(&exp.to_be_bytes()); + mant >>= 64 - _n; + buf.extend_from_slice(&mant.to_be_bytes()) + } + + buf +} + +fn extract_float_components(bits: u64) -> (u64, i64, u64) { + // Define masks for sign, exponent, and mantissa + let sign_mask: u64 = 0x8000000000000000; + let exponent_mask: u64 = 0x7FF0000000000000; + let mantissa_mask: u64 = 0x000FFFFFFFFFFFFF; + + // Extract sign + let sign = (bits & sign_mask) >> 63; + + // Extract exponent + let mut exponent = ((bits & exponent_mask) >> 52) as i64; + // Adjust for bias + exponent -= 1023; + + // Extract mantissa + let mut mantissa = bits & mantissa_mask; + + // Add implicit leading bit + mantissa |= 0x0010000000000000; + + (sign, exponent, mantissa) +} + +fn num_bits(mut n: u64) -> usize { + if n == 0 { + return 1; // Special case for 0 + } + + let mut bits = 0; + while n > 0 { + bits += 1; + n >>= 1; // Shift right to check the next bit + } + bits }