diff --git a/framework/base/src/types/interaction/managed_arg_buffer.rs b/framework/base/src/types/interaction/managed_arg_buffer.rs index ee8dd1fadd..1a465a8993 100644 --- a/framework/base/src/types/interaction/managed_arg_buffer.rs +++ b/framework/base/src/types/interaction/managed_arg_buffer.rs @@ -1,6 +1,6 @@ use crate::{ abi::{TypeAbi, TypeAbiFrom, TypeName}, - api::{ErrorApi, ManagedTypeApi, use_raw_handle}, + api::{ErrorApi, ManagedTypeApi, ManagedTypeApiImpl, use_raw_handle}, codec::{ DecodeErrorHandler, EncodeErrorHandler, NestedDecode, NestedDecodeInput, NestedEncode, NestedEncodeOutput, TopDecode, TopDecodeInput, TopEncode, TopEncodeMultiOutput, @@ -340,6 +340,10 @@ where fn save_to_payload(self, payload: &mut Self::PAYLOAD) { self.data.save_to_payload(payload); } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } impl PartialEq for ManagedArgBuffer { diff --git a/framework/base/src/types/managed/multi_value/egld_or_esdt_token_payment_multi_value.rs b/framework/base/src/types/managed/multi_value/egld_or_esdt_token_payment_multi_value.rs index 08e1822a4b..e5a5730ee1 100644 --- a/framework/base/src/types/managed/multi_value/egld_or_esdt_token_payment_multi_value.rs +++ b/framework/base/src/types/managed/multi_value/egld_or_esdt_token_payment_multi_value.rs @@ -1,5 +1,6 @@ use crate::{ abi::TypeAbiFrom, + api::ManagedTypeApiImpl, codec::{ DecodeErrorHandler, EncodeErrorHandler, MultiValueConstLength, TopDecodeMulti, TopDecodeMultiInput, TopEncodeMulti, TopEncodeMultiOutput, multi_types::MultiValue3, @@ -50,6 +51,10 @@ impl ManagedVecItem for EgldOrEsdtTokenPaymentMultiValue { fn save_to_payload(self, payload: &mut Self::PAYLOAD) { self.obj.save_to_payload(payload); } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } impl TopEncodeMulti for EgldOrEsdtTokenPaymentMultiValue diff --git a/framework/base/src/types/managed/multi_value/esdt_token_payment_multi_value.rs b/framework/base/src/types/managed/multi_value/esdt_token_payment_multi_value.rs index e71fab9ca3..3cb0ca2e2b 100644 --- a/framework/base/src/types/managed/multi_value/esdt_token_payment_multi_value.rs +++ b/framework/base/src/types/managed/multi_value/esdt_token_payment_multi_value.rs @@ -1,5 +1,6 @@ use crate::{ abi::TypeAbiFrom, + api::ManagedTypeApiImpl, codec::{ DecodeErrorHandler, EncodeErrorHandler, MultiValueConstLength, TopDecodeMulti, TopDecodeMultiInput, TopEncodeMulti, TopEncodeMultiOutput, multi_types::MultiValue3, @@ -56,6 +57,10 @@ impl ManagedVecItem for EsdtTokenPaymentMultiValue { fn save_to_payload(self, payload: &mut Self::PAYLOAD) { self.obj.save_to_payload(payload); } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } impl TopEncodeMulti for EsdtTokenPaymentMultiValue diff --git a/framework/base/src/types/managed/multi_value/multi_value_managed_vec.rs b/framework/base/src/types/managed/multi_value/multi_value_managed_vec.rs index 0bc604249b..73d2d0ae1a 100644 --- a/framework/base/src/types/managed/multi_value/multi_value_managed_vec.rs +++ b/framework/base/src/types/managed/multi_value/multi_value_managed_vec.rs @@ -99,7 +99,10 @@ where } #[allow(clippy::redundant_closure)] - pub fn slice(&self, start_index: usize, end_index: usize) -> Option { + pub fn slice(&self, start_index: usize, end_index: usize) -> Option + where + T: Clone, + { self.0 .slice(start_index, end_index) .map(|value| Self(value)) diff --git a/framework/base/src/types/managed/multi_value/multi_value_managed_vec_counted.rs b/framework/base/src/types/managed/multi_value/multi_value_managed_vec_counted.rs index c2d1b02d84..77915392a3 100644 --- a/framework/base/src/types/managed/multi_value/multi_value_managed_vec_counted.rs +++ b/framework/base/src/types/managed/multi_value/multi_value_managed_vec_counted.rs @@ -2,7 +2,7 @@ use core::borrow::Borrow; use crate::{ abi::{TypeAbi, TypeAbiFrom, TypeDescriptionContainer, TypeName}, - api::ManagedTypeApi, + api::{ManagedTypeApi, ManagedTypeApiImpl}, codec::{ DecodeErrorHandler, EncodeErrorHandler, TopDecodeMulti, TopDecodeMultiInput, TopEncodeMulti, TopEncodeMultiOutput, @@ -111,6 +111,10 @@ where fn save_to_payload(self, payload: &mut Self::PAYLOAD) { self.contents.save_to_payload(payload); } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } impl TopEncodeMulti for MultiValueManagedVecCounted diff --git a/framework/base/src/types/managed/multi_value/payment_multi_value.rs b/framework/base/src/types/managed/multi_value/payment_multi_value.rs index 193124c430..709c4d125d 100644 --- a/framework/base/src/types/managed/multi_value/payment_multi_value.rs +++ b/framework/base/src/types/managed/multi_value/payment_multi_value.rs @@ -1,5 +1,6 @@ use crate::{ abi::TypeAbiFrom, + api::ManagedTypeApiImpl, codec::{ DecodeErrorHandler, EncodeErrorHandler, MultiValueConstLength, TopDecodeMulti, TopDecodeMultiInput, TopEncodeMulti, TopEncodeMultiOutput, multi_types::MultiValue3, @@ -50,6 +51,10 @@ impl ManagedVecItem for PaymentMultiValue { fn save_to_payload(self, payload: &mut Self::PAYLOAD) { self.obj.save_to_payload(payload); } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } impl TopEncodeMulti for PaymentMultiValue diff --git a/framework/base/src/types/managed/wrapped/managed_decimal.rs b/framework/base/src/types/managed/wrapped/managed_decimal.rs index 34d41b269f..5b1682776a 100644 --- a/framework/base/src/types/managed/wrapped/managed_decimal.rs +++ b/framework/base/src/types/managed/wrapped/managed_decimal.rs @@ -17,7 +17,7 @@ pub use managed_decimal_signed::ManagedDecimalSigned; use crate::{ abi::{TypeAbi, TypeAbiFrom, TypeName}, - api::ManagedTypeApi, + api::{ManagedTypeApi, ManagedTypeApiImpl}, formatter::{FormatBuffer, FormatByteReceiver, SCDisplay}, typenum::{U4, U8, Unsigned}, types::BigUint, @@ -167,6 +167,10 @@ impl ManagedVecItem for ManagedDecimal { managed_vec_item_save_to_payload_index(self.decimals, payload, &mut index); } } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } impl ManagedVecItem @@ -189,6 +193,10 @@ impl ManagedVecItem fn save_to_payload(self, payload: &mut Self::PAYLOAD) { self.data.save_to_payload(payload); } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } impl TopEncode diff --git a/framework/base/src/types/managed/wrapped/managed_decimal/managed_decimal_signed.rs b/framework/base/src/types/managed/wrapped/managed_decimal/managed_decimal_signed.rs index 06799395ad..6ab0133088 100644 --- a/framework/base/src/types/managed/wrapped/managed_decimal/managed_decimal_signed.rs +++ b/framework/base/src/types/managed/wrapped/managed_decimal/managed_decimal_signed.rs @@ -2,7 +2,7 @@ use crate::{ abi::{TypeAbi, TypeAbiFrom, TypeName}, api::{ BigFloatApiImpl, BigIntApiImpl, HandleConstraints, ManagedBufferApiImpl, ManagedTypeApi, - const_handles, use_raw_handle, + ManagedTypeApiImpl, const_handles, use_raw_handle, }, err_msg, formatter::{FormatBuffer, FormatByteReceiver, SCDisplay}, @@ -217,6 +217,10 @@ impl ManagedVecItem for ManagedDecimalSigned managed_vec_item_save_to_payload_index(self.decimals, payload, &mut index); } } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } impl ManagedVecItem @@ -239,6 +243,10 @@ impl ManagedVecItem fn save_to_payload(self, payload: &mut Self::PAYLOAD) { self.data.save_to_payload(payload); } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } impl TopEncode diff --git a/framework/base/src/types/managed/wrapped/managed_option.rs b/framework/base/src/types/managed/wrapped/managed_option.rs index 62006035b1..095e815069 100644 --- a/framework/base/src/types/managed/wrapped/managed_option.rs +++ b/framework/base/src/types/managed/wrapped/managed_option.rs @@ -4,7 +4,7 @@ use generic_array::typenum::U4; use crate::{ abi::TypeAbiFrom, - api::HandleConstraints, + api::{HandleConstraints, ManagedTypeApiImpl}, codec::{ DecodeErrorHandler, EncodeErrorHandler, NestedDecode, NestedDecodeInput, NestedEncode, NestedEncodeOutput, TopDecode, TopDecodeInput, TopEncode, TopEncodeOutput, @@ -221,6 +221,10 @@ where fn save_to_payload(self, payload: &mut Self::PAYLOAD) { self.handle.get_raw_handle().save_to_payload(payload); } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } impl TopEncode for ManagedOption diff --git a/framework/base/src/types/managed/wrapped/managed_vec.rs b/framework/base/src/types/managed/wrapped/managed_vec.rs index 3d72783674..314b410772 100644 --- a/framework/base/src/types/managed/wrapped/managed_vec.rs +++ b/framework/base/src/types/managed/wrapped/managed_vec.rs @@ -77,6 +77,7 @@ where M: ManagedTypeApi, T: ManagedVecItem, { + /// Creates a new, empty `ManagedVec`. #[inline] pub fn new() -> Self { ManagedVec { @@ -162,6 +163,7 @@ where self.byte_len() / T::payload_size() } + /// Returns `true` if the vec contains no elements. #[inline] pub fn is_empty(&self) -> bool { self.byte_len() == 0 @@ -183,6 +185,7 @@ where true } + /// Retrieves the element at `index`, or `None` if the index is out of range. pub fn try_get(&self, index: usize) -> Option> { let mut payload = T::PAYLOAD::new_buffer(); if self.load_item_payload(index, &mut payload) { @@ -236,6 +239,9 @@ where } } + /// Returns a mutable guard for the element at `index`, allowing in-place modification. + /// + /// Signals an error and terminates execution if the index is out of range. pub fn get_mut(&mut self, index: usize) -> ManagedVecRefMut<'_, M, T> { ManagedVecRefMut::new(self.get_handle(), index) } @@ -249,61 +255,138 @@ where } } - pub fn set(&mut self, index: usize, item: T) -> Result<(), InvalidSliceError> { + pub(crate) unsafe fn set_unchecked_no_drop( + &mut self, + index: usize, + item: T, + ) -> Result<(), InvalidSliceError> { let byte_index = index * T::payload_size(); let mut payload = T::PAYLOAD::new_buffer(); item.save_to_payload(&mut payload); self.buffer.set_slice(byte_index, payload.payload_slice()) } + /// Replaces the element at `index` with `item`, returning the displaced element. + /// + /// Signals an error and terminates execution if the index is out of range. + pub fn set(&mut self, index: usize, item: T) -> Result { + let old_item = unsafe { self.get_unsafe(index) }; + unsafe { + self.set_unchecked_no_drop(index, item)?; + } + Ok(old_item) + } + /// Returns a new `ManagedVec`, containing the [start_index, end_index) range of elements. - /// Returns `None` if any index is out of range - pub fn slice(&self, start_index: usize, end_index: usize) -> Option { + /// Returns `None` if any index is out of range. + /// + /// Note: for managed types that require handle-level drop (e.g. under `StaticApi`), + /// this performs a deep copy of each item so that both the original and the slice + /// hold independently owned handles. + pub fn slice(&self, start_index: usize, end_index: usize) -> Option + where + T: Clone, + { + if start_index > end_index || end_index > self.len() { + return None; + } + + if T::requires_drop() { + // Copying raw bytes would alias the handle integers, causing double-frees on drop. + // Build the slice by cloning individual items instead. + + let mut result = ManagedVec::new(); + for i in start_index..end_index { + // self.get(i) is a borrow (non-owning ManagedRef), .borrow() yields &T. + // .clone() allocates a fresh VM object with an independent handle, + // so both the original vec and the return value own distinct handles. + result.push(self.get(i).borrow().clone()); + } + Some(result) + } else { + unsafe { self.slice_no_copy_unchecked(start_index, end_index) } + } + } + + /// Returns a new `ManagedVec`, containing the [start_index, end_index) range of elements. + /// Returns `None` if any index is out of range. + /// + /// # Safety + /// + /// Only safe when `T::requires_drop() == false` (e.g. all non-`StaticApi` backends). + /// In all other cases, both the original vec and the returned slice will hold aliased + /// handle integers, and both will attempt to free those handles on drop — causing a + /// double-free. Use the safe [`slice`] method instead. + unsafe fn slice_no_copy_unchecked(&self, start_index: usize, end_index: usize) -> Option { let byte_start = start_index * T::payload_size(); let byte_end = end_index * T::payload_size(); let opt_buffer = self.buffer.copy_slice(byte_start, byte_end - byte_start); opt_buffer.map(ManagedVec::new_from_raw_buffer) } + /// Appends `item` to the back of the vec. pub fn push(&mut self, item: T) { let mut payload = T::PAYLOAD::new_buffer(); item.save_to_payload(&mut payload); self.buffer.append_bytes(payload.payload_slice()); } - pub fn remove(&mut self, index: usize) { - let len = self.len(); - if index >= len { - M::error_api_impl().signal_error(INDEX_OUT_OF_RANGE_MSG); - } - - let part_before = if index > 0 { - match self.slice(0, index) { - Some(s) => s, - None => M::error_api_impl().signal_error(INDEX_OUT_OF_RANGE_MSG), - } - } else { - ManagedVec::new() - }; - let part_after = if index < len { - match self.slice(index + 1, len) { - Some(s) => s, - None => M::error_api_impl().signal_error(INDEX_OUT_OF_RANGE_MSG), - } + /// Removes the slot at `index` from the buffer without dropping the item stored there. + /// + /// Callers must ensure the item has already been extracted (and will be dropped separately). + fn strip_index(&mut self, index: usize, len: usize) { + // Rebuild the buffer at the raw ManagedBuffer level. Creating intermediate + // ManagedVec slices would alias the surviving item handles and cause double-drops + // when those slices are dropped. + let payload_size = T::payload_size(); + let byte_index = index * payload_size; + let byte_after = (index + 1) * payload_size; + let byte_total = len * payload_size; + + let mut new_buffer = if byte_index > 0 { + self.buffer + .copy_slice(0, byte_index) + .unwrap_or_else(|| M::error_api_impl().signal_error(INDEX_OUT_OF_RANGE_MSG)) } else { - ManagedVec::new() + ManagedBuffer::new() }; + if byte_after < byte_total { + let after = self + .buffer + .copy_slice(byte_after, byte_total - byte_after) + .unwrap_or_else(|| M::error_api_impl().signal_error(INDEX_OUT_OF_RANGE_MSG)); + new_buffer.append(&after); + } - *self = part_before; - self.buffer.append(&part_after.buffer); + // Assigning self.buffer only invokes ManagedBuffer::drop for the old byte array + // (freeing the buffer handle itself), not the item handles stored inside. + // The surviving item handles are now in new_buffer and will be freed exactly once + // when self is eventually dropped via ManagedVec::drop. + self.buffer = new_buffer; } + /// Removes and returns the element at `index`, shifting the remaining elements. + /// + /// Signals an error and terminates execution if the index is out of range. pub fn take(&mut self, index: usize) -> T { + let len = self.len(); + if index >= len { + M::error_api_impl().signal_error(INDEX_OUT_OF_RANGE_MSG); + } let item = unsafe { self.get_unsafe(index) }; - self.remove(index); + // strip_index, not remove: the item is already extracted above and must not be + // dropped a second time. + self.strip_index(index, len); item } + /// Removes and drops the element at `index`, shifting the remaining elements. + /// + /// Signals an error and terminates execution if the index is out of range. + pub fn remove(&mut self, index: usize) { + let _ = self.take(index); + } + /// New `ManagedVec` instance with 1 element in it. pub fn from_single_item(item: T) -> Self { let mut result = ManagedVec::new(); @@ -311,7 +394,12 @@ where result } + /// Replaces all contents of the vec with a single item, dropping all previous elements. pub fn overwrite_with_single_item(&mut self, item: T) { + unsafe { + self.drop_items(); + } + let mut payload = T::PAYLOAD::new_buffer(); item.save_to_payload(&mut payload); self.buffer.overwrite(payload.payload_slice()); @@ -319,15 +407,26 @@ where /// Appends all the contents of another managed vec at the end of the current one. /// Consumes the other vec in the process. - pub fn append_vec(&mut self, item: ManagedVec) { - self.buffer.append(&item.buffer); + pub fn append_vec(&mut self, v: ManagedVec) { + self.buffer.append(&v.buffer); + // The items are now owned by self, so we must not drop them again. + // We do need to drop the buffer handle itself, so we extract it and forget the vec. + unsafe { + let buffer = core::ptr::read(&v.buffer); + core::mem::forget(v); + core::mem::drop(buffer); + } } /// Removes all items while retaining the handle. pub fn clear(&mut self) { + unsafe { + self.drop_items(); + } self.buffer.overwrite(&[]); } + /// Converts the `ManagedVec` into a standard `Vec`, consuming the vec in the process. #[cfg(feature = "alloc")] pub fn into_vec(self) -> Vec { let mut v = Vec::new(); @@ -357,11 +456,12 @@ where result } + /// Returns an iterator over shared references to the elements of the vec. pub fn iter(&self) -> ManagedVecRefIterator<'_, M, T> { ManagedVecRefIterator::new(self) } - /// Creates a reference to and identical object, but one which behaves like a multi-value-vec. + /// Creates a reference to an identical object, but one which behaves like a multi-value-vec. pub fn as_multi(&self) -> &MultiValueManagedVec { MultiValueManagedVec::transmute_from_handle_ref(&self.buffer.handle) } @@ -557,7 +657,17 @@ where } } - let (dedup, _) = slice.split_at_mut(next_write); + let (dedup, tail) = slice.split_at_mut(next_write); + // Drop the duplicate items moved into the tail, so their handles are freed + // before the buffer is truncated to the dedup length. + if T::requires_drop() { + for tail_item in tail.iter() { + unsafe { + let item = T::read_from_payload(&tail_item.encoded); + core::mem::drop(item); + } + } + } dedup }) } @@ -648,12 +758,12 @@ where } } -impl Drop for ManagedVec +impl ManagedVec where M: ManagedTypeApi, T: ManagedVecItem, { - fn drop(&mut self) { + unsafe fn drop_items(&mut self) { unsafe { if T::requires_drop() { let iter = ManagedVecPayloadIterator::::new(self.get_handle()); @@ -666,6 +776,20 @@ where } } +impl Drop for ManagedVec +where + M: ManagedTypeApi, + T: ManagedVecItem, +{ + fn drop(&mut self) { + // We need to drop each item, to allow for proper cleanup of resources. + // After that, the buffer itself can be dropped, which will free the handle and the underlying resource if needed. + unsafe { + self.drop_items(); + } + } +} + impl TopEncode for ManagedVec where M: ManagedTypeApi, diff --git a/framework/base/src/types/managed/wrapped/managed_vec_item.rs b/framework/base/src/types/managed/wrapped/managed_vec_item.rs index bbb235bdec..e1da2bdddd 100644 --- a/framework/base/src/types/managed/wrapped/managed_vec_item.rs +++ b/framework/base/src/types/managed/wrapped/managed_vec_item.rs @@ -74,9 +74,7 @@ pub trait ManagedVecItem: Sized + 'static { /// Signals that vec should drop all items one by one when being itself dropped. /// /// If false, iterating over all items on drop makes no sense. - fn requires_drop() -> bool { - false - } + fn requires_drop() -> bool; fn temp_decode(payload: &Self::PAYLOAD, f: F) -> R where @@ -145,6 +143,10 @@ macro_rules! impl_int { fn save_to_payload(self, payload: &mut Self::PAYLOAD) { payload.buffer = GenericArray::from_array(self.to_be_bytes()); } + + fn requires_drop() -> bool { + false + } } }; } @@ -171,6 +173,10 @@ impl ManagedVecItem for usize { fn save_to_payload(self, payload: &mut Self::PAYLOAD) { (self as u32).save_to_payload(payload); } + + fn requires_drop() -> bool { + false + } } impl ManagedVecItem for bool { @@ -191,6 +197,10 @@ impl ManagedVecItem for bool { // false -> 0u8 u8::from(self).save_to_payload(payload); } + + fn requires_drop() -> bool { + false + } } impl ManagedVecItem for Option @@ -259,6 +269,10 @@ macro_rules! impl_managed_type { let handle = unsafe { self.forget_into_handle() }; handle.get_raw_handle().save_to_payload(payload); } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } }; } @@ -350,6 +364,10 @@ impl ManagedVecItem for EsdtTokenType { fn save_to_payload(self, payload: &mut Self::PAYLOAD) { self.as_u8().save_to_payload(payload); } + + fn requires_drop() -> bool { + false + } } impl ManagedVecItem for EsdtLocalRole { @@ -368,6 +386,10 @@ impl ManagedVecItem for EsdtLocalRole { fn save_to_payload(self, payload: &mut Self::PAYLOAD) { self.as_u16().save_to_payload(payload); } + + fn requires_drop() -> bool { + false + } } impl ManagedVecItem for MultiValue2 @@ -404,6 +426,10 @@ where managed_vec_item_save_to_payload_index(tuple.1, payload, &mut index); } } + + fn requires_drop() -> bool { + T1::requires_drop() || T2::requires_drop() + } } impl ManagedVecItem for MultiValue3 @@ -443,4 +469,8 @@ where managed_vec_item_save_to_payload_index(tuple.2, payload, &mut index); } } + + fn requires_drop() -> bool { + T1::requires_drop() || T2::requires_drop() || T3::requires_drop() + } } diff --git a/framework/base/src/types/managed/wrapped/managed_vec_ref_mut.rs b/framework/base/src/types/managed/wrapped/managed_vec_ref_mut.rs index b5abaaa2df..b34087ed8c 100644 --- a/framework/base/src/types/managed/wrapped/managed_vec_ref_mut.rs +++ b/framework/base/src/types/managed/wrapped/managed_vec_ref_mut.rs @@ -55,13 +55,14 @@ where fn drop(&mut self) { // This drop saves the item back into the parent ManagedVec. // - // The `set` method also handles soft deallocation - // (freeing of the handle, without deallocating the underlying resource). + // Using `set_unchecked_no_drop` ensures the item's data is written back + // without running Drop on the item itself, so the handle is transferred + // to the buffer rather than freed. let item = unsafe { ManuallyDrop::take(&mut self.item) }; unsafe { let mut parent_ref = ManagedRefMut::>::wrap_handle(self.managed_vec_handle.clone()); - let _ = parent_ref.set(self.item_index, item); + let _ = parent_ref.set_unchecked_no_drop(self.item_index, item); } } } diff --git a/framework/base/src/types/managed/wrapped/token/egld_or_esdt_token_payment.rs b/framework/base/src/types/managed/wrapped/token/egld_or_esdt_token_payment.rs index 6e5e80a477..a72da725f3 100644 --- a/framework/base/src/types/managed/wrapped/token/egld_or_esdt_token_payment.rs +++ b/framework/base/src/types/managed/wrapped/token/egld_or_esdt_token_payment.rs @@ -3,7 +3,7 @@ use multiversx_sc_codec::IntoMultiValue; use crate::{ abi::TypeAbiFrom, - api::ManagedTypeApi, + api::{ManagedTypeApi, ManagedTypeApiImpl}, types::{ BigUint, EgldOrEsdtTokenIdentifier, EgldOrEsdtTokenPaymentMultiValue, EgldOrEsdtTokenPaymentRefs, EsdtTokenPayment, EsdtTokenPaymentRefs, ManagedVecItem, @@ -178,4 +178,8 @@ impl ManagedVecItem for EgldOrEsdtTokenPayment { managed_vec_item_save_to_payload_index(self.amount, payload, &mut index); } } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } diff --git a/framework/base/src/types/managed/wrapped/token/esdt_token_payment.rs b/framework/base/src/types/managed/wrapped/token/esdt_token_payment.rs index e914d8a480..2c6cfd5c90 100644 --- a/framework/base/src/types/managed/wrapped/token/esdt_token_payment.rs +++ b/framework/base/src/types/managed/wrapped/token/esdt_token_payment.rs @@ -1,7 +1,7 @@ use generic_array::typenum::U16; use crate::{ - api::ManagedTypeApi, + api::{ManagedTypeApi, ManagedTypeApiImpl}, types::{ BigUint, EsdtTokenIdentifier, EsdtTokenPaymentMultiValue, EsdtTokenType, ManagedType, ManagedVec, ManagedVecItem, ManagedVecItemPayloadBuffer, Payment, PaymentVec, Ref, @@ -248,6 +248,10 @@ impl ManagedVecItem for EsdtTokenPayment { managed_vec_item_save_to_payload_index(self.amount, payload, &mut index); } } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } /// The version of `EsdtTokenPayment` that contains references instead of owned fields. diff --git a/framework/base/src/types/managed/wrapped/token/fungible_payment.rs b/framework/base/src/types/managed/wrapped/token/fungible_payment.rs index 92ce55f6d3..ebac98d273 100644 --- a/framework/base/src/types/managed/wrapped/token/fungible_payment.rs +++ b/framework/base/src/types/managed/wrapped/token/fungible_payment.rs @@ -2,7 +2,7 @@ use generic_array::typenum::U8; use crate::{ abi::{TypeAbi, TypeAbiFrom, TypeName}, - api::ManagedTypeApi, + api::{ManagedTypeApi, ManagedTypeApiImpl}, codec::{ self, derive::{NestedDecode, NestedEncode, TopDecode, TopEncode}, @@ -78,4 +78,8 @@ impl ManagedVecItem for FungiblePayment { managed_vec_item_save_to_payload_index(self.amount, payload, &mut index); } } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } diff --git a/framework/base/src/types/managed/wrapped/token/payment.rs b/framework/base/src/types/managed/wrapped/token/payment.rs index a9c7cdf6cb..8e2164254d 100644 --- a/framework/base/src/types/managed/wrapped/token/payment.rs +++ b/framework/base/src/types/managed/wrapped/token/payment.rs @@ -3,7 +3,7 @@ use multiversx_sc_codec::IntoMultiValue; use crate::{ abi::{TypeAbi, TypeAbiFrom, TypeName}, - api::{ErrorApiImpl, ManagedTypeApi}, + api::{ErrorApiImpl, ManagedTypeApi, ManagedTypeApiImpl}, codec::{ self, NestedDecode, TopDecode, derive::{NestedEncode, TopEncode}, @@ -337,4 +337,8 @@ impl ManagedVecItem for Payment { managed_vec_item_save_to_payload_index(self.amount, payload, &mut index); } } + + fn requires_drop() -> bool { + M::managed_type_impl().requires_managed_type_drop() + } } diff --git a/framework/derive/src/managed_vec_item_derive.rs b/framework/derive/src/managed_vec_item_derive.rs index 4f7b328bab..c36c6822fa 100644 --- a/framework/derive/src/managed_vec_item_derive.rs +++ b/framework/derive/src/managed_vec_item_derive.rs @@ -38,6 +38,39 @@ fn generate_enum_payload_nested_tuple(data_enum: &syn::DataEnum) -> proc_macro2: result } +fn generate_requires_drop_snippets(fields: &syn::Fields) -> Vec { + match fields { + syn::Fields::Named(fields_named) => fields_named + .named + .iter() + .map(|field| { + let type_name = &field.ty; + quote! { + <#type_name as multiversx_sc::types::ManagedVecItem>::requires_drop() + } + }) + .collect(), + _ => { + panic!("ManagedVecItem only supports named fields") + } + } +} + +fn generate_enum_requires_drop_snippets( + data_enum: &syn::DataEnum, +) -> Vec { + data_enum + .variants + .iter() + .filter_map(|variant| single_fields_type(&variant.fields)) + .map(|ty| { + quote! { + <#ty as multiversx_sc::types::ManagedVecItem>::requires_drop() + } + }) + .collect() +} + fn generate_skips_reserialization_snippets(fields: &syn::Fields) -> Vec { match fields { syn::Fields::Named(fields_named) => fields_named @@ -104,6 +137,7 @@ fn enum_derive(data_enum: &syn::DataEnum, ast: &syn::DeriveInput) -> TokenStream let (impl_generics, ty_generics, where_clause) = &ast.generics.split_for_impl(); let payload_nested_tuple = generate_enum_payload_nested_tuple(data_enum); let skips_reserialization = !variants_have_fields(data_enum); + let requires_drop_snippets = generate_enum_requires_drop_snippets(data_enum); let mut reader_match_arms = Vec::::new(); let mut writer_match_arms = Vec::::new(); @@ -186,6 +220,10 @@ fn enum_derive(data_enum: &syn::DataEnum, ast: &syn::DeriveInput) -> TokenStream }; } } + + fn requires_drop() -> bool { + false #(|| #requires_drop_snippets)* + } } }; result.into() @@ -216,6 +254,7 @@ fn struct_derive(data_struct: &syn::DataStruct, ast: &syn::DeriveInput) -> Token let payload_nested_tuple = generate_struct_payload_nested_tuple(&data_struct.fields); let skips_reserialization_snippets = generate_skips_reserialization_snippets(&data_struct.fields); + let requires_drop_snippets = generate_requires_drop_snippets(&data_struct.fields); let read_from_payload_snippets = generate_read_from_payload_snippets(&data_struct.fields); let save_to_payload_snippets = generate_save_to_payload_snippets(&data_struct.fields); @@ -245,6 +284,10 @@ fn struct_derive(data_struct: &syn::DataStruct, ast: &syn::DeriveInput) -> Token #(#save_to_payload_snippets)* } } + + fn requires_drop() -> bool { + false #(|| #requires_drop_snippets)* + } } }; result.into() diff --git a/framework/scenario/src/api/impl_vh/debug_api.rs b/framework/scenario/src/api/impl_vh/debug_api.rs index 7f65e51b17..b542f55942 100644 --- a/framework/scenario/src/api/impl_vh/debug_api.rs +++ b/framework/scenario/src/api/impl_vh/debug_api.rs @@ -101,6 +101,10 @@ This can sometimes happen during whitebox testing if the objects are mixed betwe let top_static_vars = ContractDebugStack::static_peek().static_var_ref; f(&top_static_vars) } + + fn backend_requires_managed_type_drop() -> bool { + false + } } pub type DebugApi = VMHooksApi; diff --git a/framework/scenario/src/api/impl_vh/single_tx_api.rs b/framework/scenario/src/api/impl_vh/single_tx_api.rs index 89690c76c5..9776f78b20 100644 --- a/framework/scenario/src/api/impl_vh/single_tx_api.rs +++ b/framework/scenario/src/api/impl_vh/single_tx_api.rs @@ -41,6 +41,10 @@ impl VMHooksApiBackend for SingleTxApiBackend { { SINGLE_TX_API_STATIC_CELL.with(|data| f(data)) } + + fn backend_requires_managed_type_drop() -> bool { + false + } } /// Similar to the `StaticApi`, but offers allows calls to storage, input, and even creating results. diff --git a/framework/scenario/src/api/impl_vh/static_api.rs b/framework/scenario/src/api/impl_vh/static_api.rs index 9f335982f9..613ca3f441 100644 --- a/framework/scenario/src/api/impl_vh/static_api.rs +++ b/framework/scenario/src/api/impl_vh/static_api.rs @@ -44,6 +44,10 @@ impl VMHooksApiBackend for StaticApiBackend { f(&data) }) } + + fn backend_requires_managed_type_drop() -> bool { + true + } } pub type StaticApi = VMHooksApi; diff --git a/framework/scenario/src/api/impl_vh/vm_hooks_backend.rs b/framework/scenario/src/api/impl_vh/vm_hooks_backend.rs index 2db3ab789b..fc6caeb08d 100644 --- a/framework/scenario/src/api/impl_vh/vm_hooks_backend.rs +++ b/framework/scenario/src/api/impl_vh/vm_hooks_backend.rs @@ -56,4 +56,6 @@ pub trait VMHooksApiBackend: Clone + Send + Sync + 'static { fn with_static_data(f: F) -> R where F: FnOnce(&StaticVarData) -> R; + + fn backend_requires_managed_type_drop() -> bool; } diff --git a/framework/scenario/src/api/managed_type_api_vh.rs b/framework/scenario/src/api/managed_type_api_vh.rs index 7cd7760c48..2e5d95174b 100644 --- a/framework/scenario/src/api/managed_type_api_vh.rs +++ b/framework/scenario/src/api/managed_type_api_vh.rs @@ -128,6 +128,10 @@ impl ManagedTypeApiImpl for VMHooksApi { i32_to_bool(result) } + fn requires_managed_type_drop(&self) -> bool { + VHB::backend_requires_managed_type_drop() + } + fn drop_managed_buffer(&self, handle: Self::ManagedBufferHandle) { self.with_vm_hooks_ctx_if_active(&handle, |vh| { vh.drop_managed_buffer(handle.get_raw_handle_unchecked()) diff --git a/framework/scenario/tests/derive_managed_vec_item_biguint_test.rs b/framework/scenario/tests/derive_managed_vec_item_biguint_test.rs index ce64e9fe75..fd0e293d49 100644 --- a/framework/scenario/tests/derive_managed_vec_item_biguint_test.rs +++ b/framework/scenario/tests/derive_managed_vec_item_biguint_test.rs @@ -31,6 +31,7 @@ fn struct_with_numbers_static() { assert!( ! as multiversx_sc::types::ManagedVecItem>::SKIPS_RESERIALIZATION ); + assert!( as multiversx_sc::types::ManagedVecItem>::requires_drop()); } #[test] diff --git a/framework/scenario/tests/derive_managed_vec_item_decimal_test.rs b/framework/scenario/tests/derive_managed_vec_item_decimal_test.rs index 4c2016b41a..65d38826af 100644 --- a/framework/scenario/tests/derive_managed_vec_item_decimal_test.rs +++ b/framework/scenario/tests/derive_managed_vec_item_decimal_test.rs @@ -37,6 +37,7 @@ fn struct_with_decimal_static() { assert!( ! as multiversx_sc::types::ManagedVecItem>::SKIPS_RESERIALIZATION ); + assert!( as multiversx_sc::types::ManagedVecItem>::requires_drop()); } #[test] diff --git a/framework/scenario/tests/derive_managed_vec_item_enum_1.rs b/framework/scenario/tests/derive_managed_vec_item_enum_1.rs index b2eb71bdaa..1cf304a911 100644 --- a/framework/scenario/tests/derive_managed_vec_item_enum_1.rs +++ b/framework/scenario/tests/derive_managed_vec_item_enum_1.rs @@ -27,6 +27,7 @@ fn enum_static() { 9 ); assert!(!::SKIPS_RESERIALIZATION); + assert!(!::requires_drop()); } #[test] diff --git a/framework/scenario/tests/derive_managed_vec_item_enum_2_managed.rs b/framework/scenario/tests/derive_managed_vec_item_enum_2_managed.rs index f024cc15af..2cae758239 100644 --- a/framework/scenario/tests/derive_managed_vec_item_enum_2_managed.rs +++ b/framework/scenario/tests/derive_managed_vec_item_enum_2_managed.rs @@ -34,6 +34,9 @@ fn enum_static() { assert!( ! as multiversx_sc::types::ManagedVecItem>::SKIPS_RESERIALIZATION ); + assert!( + as multiversx_sc::types::ManagedVecItem>::requires_drop() + ); } #[test] diff --git a/framework/scenario/tests/derive_managed_vec_item_enum_simple.rs b/framework/scenario/tests/derive_managed_vec_item_enum_simple.rs index f5083ce29c..1795d3fa78 100644 --- a/framework/scenario/tests/derive_managed_vec_item_enum_simple.rs +++ b/framework/scenario/tests/derive_managed_vec_item_enum_simple.rs @@ -26,6 +26,7 @@ fn enum_static() { 1 ); assert!(::SKIPS_RESERIALIZATION); + assert!(!::requires_drop()); } #[test] diff --git a/framework/scenario/tests/derive_managed_vec_item_esdt_token_payment_test.rs b/framework/scenario/tests/derive_managed_vec_item_esdt_token_payment_test.rs index e0ca8f4279..da6eb1c70b 100644 --- a/framework/scenario/tests/derive_managed_vec_item_esdt_token_payment_test.rs +++ b/framework/scenario/tests/derive_managed_vec_item_esdt_token_payment_test.rs @@ -37,6 +37,10 @@ fn struct_with_numbers_static() { assert!( ! as multiversx_sc::types::ManagedVecItem>::SKIPS_RESERIALIZATION ); + assert!( + as multiversx_sc::types::ManagedVecItem>::requires_drop( + ) + ); } #[test] diff --git a/framework/scenario/tests/derive_managed_vec_item_struct_1_test.rs b/framework/scenario/tests/derive_managed_vec_item_struct_1_test.rs index 88204e1dde..32e0491f57 100644 --- a/framework/scenario/tests/derive_managed_vec_item_struct_1_test.rs +++ b/framework/scenario/tests/derive_managed_vec_item_struct_1_test.rs @@ -27,6 +27,7 @@ fn struct_1_static() { 16 ); assert!(::SKIPS_RESERIALIZATION); + assert!(!::requires_drop()); } /// The reason we are including a codec test here is that because of the SKIPS_RESERIALIZATION flag, diff --git a/framework/scenario/tests/derive_managed_vec_item_struct_2_test.rs b/framework/scenario/tests/derive_managed_vec_item_struct_2_test.rs index f6728c7874..5095055ecb 100644 --- a/framework/scenario/tests/derive_managed_vec_item_struct_2_test.rs +++ b/framework/scenario/tests/derive_managed_vec_item_struct_2_test.rs @@ -26,6 +26,7 @@ fn struct_2_static() { 22 ); assert!(!::SKIPS_RESERIALIZATION); + assert!(!::requires_drop()); } #[test] diff --git a/framework/scenario/tests/derive_managed_vec_item_struct_3.rs b/framework/scenario/tests/derive_managed_vec_item_struct_3.rs index 7e23c6737c..b02a30f338 100644 --- a/framework/scenario/tests/derive_managed_vec_item_struct_3.rs +++ b/framework/scenario/tests/derive_managed_vec_item_struct_3.rs @@ -46,4 +46,6 @@ fn struct_3_static() { 74 ); assert!(! as multiversx_sc::types::ManagedVecItem>::SKIPS_RESERIALIZATION); + assert!( as multiversx_sc::types::ManagedVecItem>::requires_drop()); + assert!(!::requires_drop()); } diff --git a/framework/scenario/tests/managed_vec_test.rs b/framework/scenario/tests/managed_vec_test.rs index 4c98044d71..7ff3590c87 100644 --- a/framework/scenario/tests/managed_vec_test.rs +++ b/framework/scenario/tests/managed_vec_test.rs @@ -656,3 +656,300 @@ fn test_is_single_item() { managed_vec.push(BigUint::::from(2u32)); assert!(managed_vec.is_single_item().is_none()); } + +#[test] +fn test_byte_len() { + let mut vec = ManagedVec::::new(); + assert_eq!(vec.byte_len(), 0); + vec.push(1u32); + assert_eq!(vec.byte_len(), 4); // u32 is 4 bytes + vec.push(2u32); + assert_eq!(vec.byte_len(), 8); + vec.push(3u32); + assert_eq!(vec.byte_len(), 12); +} + +#[test] +fn test_is_empty() { + let mut vec = ManagedVec::::new(); + assert!(vec.is_empty()); + vec.push(42u32); + assert!(!vec.is_empty()); +} + +#[test] +fn test_is_empty_biguint() { + let mut vec = ManagedVec::>::new(); + assert!(vec.is_empty()); + vec.push(BigUint::from(42u64)); + assert!(!vec.is_empty()); +} + +#[test] +fn test_try_get() { + let mut vec = ManagedVec::::new(); + assert!(vec.try_get(0).is_none()); + vec.push(10u32); + vec.push(20u32); + assert_eq!(vec.try_get(0).unwrap(), 10u32); + assert_eq!(vec.try_get(1).unwrap(), 20u32); + assert!(vec.try_get(2).is_none()); +} + +#[test] +fn test_try_get_biguint() { + let mut vec = ManagedVec::>::new(); + assert!(vec.try_get(0).is_none()); + vec.push(BigUint::from(10u64)); + vec.push(BigUint::from(20u64)); + assert_eq!(*vec.try_get(0).unwrap(), BigUint::from(10u64)); + assert_eq!(*vec.try_get(1).unwrap(), BigUint::from(20u64)); + assert!(vec.try_get(2).is_none()); +} + +#[test] +fn test_set_u32() { + let mut vec = ManagedVec::::new(); + vec.push(10u32); + vec.push(20u32); + vec.push(30u32); + + let old = vec.set(1, 99u32).unwrap(); + assert_eq!(old, 20u32); + assert_eq!(vec.get(0), 10u32); + assert_eq!(vec.get(1), 99u32); + assert_eq!(vec.get(2), 30u32); +} + +#[test] +fn test_set_biguint() { + let mut vec = ManagedVec::>::new(); + vec.push(BigUint::from(10u64)); + vec.push(BigUint::from(20u64)); + vec.push(BigUint::from(30u64)); + + let old = vec.set(1, BigUint::from(99u64)).unwrap(); + assert_eq!(old, BigUint::from(20u64)); + assert_eq!(*vec.get(0), BigUint::from(10u64)); + assert_eq!(*vec.get(1), BigUint::from(99u64)); + assert_eq!(*vec.get(2), BigUint::from(30u64)); +} + +#[test] +fn test_slice_u32() { + let mut vec = ManagedVec::::new(); + for i in 1u32..=5u32 { + vec.push(i); + } + + let sliced = vec.slice(1, 4).unwrap(); + assert_eq!(sliced.len(), 3); + assert_eq!(sliced.get(0), 2u32); + assert_eq!(sliced.get(1), 3u32); + assert_eq!(sliced.get(2), 4u32); + + // original is intact + assert_eq!(vec.len(), 5); + + // empty slice (start == end) + let empty = vec.slice(2, 2).unwrap(); + assert!(empty.is_empty()); + + // out of range + assert!(vec.slice(3, 10).is_none()); + + // full slice + let full = vec.slice(0, 5).unwrap(); + assert_eq!(full.len(), 5); +} + +#[test] +fn test_slice_biguint() { + let mut vec = ManagedVec::>::new(); + for i in 1u64..=5u64 { + vec.push(BigUint::from(i)); + } + + let sliced = vec.slice(1, 4).unwrap(); + assert_eq!(sliced.len(), 3); + assert_eq!(*sliced.get(0), BigUint::from(2u64)); + assert_eq!(*sliced.get(1), BigUint::from(3u64)); + assert_eq!(*sliced.get(2), BigUint::from(4u64)); + + // original is intact after slicing (items were deep-copied) + assert_eq!(vec.len(), 5); + assert_eq!(*vec.get(0), BigUint::from(1u64)); + + // out of range + assert!(vec.slice(3, 10).is_none()); +} + +#[test] +fn test_slice_out_of_bounds_u32() { + let mut vec = ManagedVec::::new(); + for i in 1u32..=5u32 { + vec.push(i); + } + + // end > len + assert!(vec.slice(0, 6).is_none()); + assert!(vec.slice(3, 6).is_none()); + + // start > end + assert!(vec.slice(3, 2).is_none()); + assert!(vec.slice(5, 1).is_none()); + + // start == end == len is a valid empty slice, not out of bounds + assert!(vec.slice(5, 5).is_some()); + assert!(vec.slice(5, 5).unwrap().is_empty()); + + // start > len + assert!(vec.slice(6, 6).is_none()); + + // empty vec + let empty = ManagedVec::::new(); + assert!(empty.slice(0, 1).is_none()); + assert!(empty.slice(1, 0).is_none()); + // empty slice from empty vec is valid + assert!(empty.slice(0, 0).is_some()); + assert!(empty.slice(0, 0).unwrap().is_empty()); +} + +#[test] +fn test_slice_out_of_bounds_biguint() { + let mut vec = ManagedVec::>::new(); + for i in 1u64..=5u64 { + vec.push(BigUint::from(i)); + } + + // end > len + assert!(vec.slice(0, 6).is_none()); + assert!(vec.slice(3, 6).is_none()); + + // start > end + assert!(vec.slice(3, 2).is_none()); + assert!(vec.slice(5, 1).is_none()); + + // start == end == len is a valid empty slice, not out of bounds + assert!(vec.slice(5, 5).is_some()); + assert!(vec.slice(5, 5).unwrap().is_empty()); + + // start > len + assert!(vec.slice(6, 6).is_none()); + + // empty vec + let empty = ManagedVec::>::new(); + assert!(empty.slice(0, 1).is_none()); + assert!(empty.slice(1, 0).is_none()); + // empty slice from empty vec is valid + assert!(empty.slice(0, 0).is_some()); + assert!(empty.slice(0, 0).unwrap().is_empty()); +} + +#[test] +fn test_remove_u32() { + let mut vec = ManagedVec::::new(); + vec.push(10u32); + vec.push(20u32); + vec.push(30u32); + + vec.remove(1); + assert_eq!(vec.len(), 2); + assert_eq!(vec.get(0), 10u32); + assert_eq!(vec.get(1), 30u32); + + vec.remove(0); + assert_eq!(vec.len(), 1); + assert_eq!(vec.get(0), 30u32); + + vec.remove(0); + assert!(vec.is_empty()); +} + +#[test] +fn test_remove_biguint() { + let mut vec = ManagedVec::>::new(); + vec.push(BigUint::from(10u64)); + vec.push(BigUint::from(20u64)); + vec.push(BigUint::from(30u64)); + + vec.remove(1); + assert_eq!(vec.len(), 2); + assert_eq!(*vec.get(0), BigUint::from(10u64)); + assert_eq!(*vec.get(1), BigUint::from(30u64)); +} + +#[test] +fn test_from_single_item_u32() { + let vec = ManagedVec::::from_single_item(42u32); + assert_eq!(vec.len(), 1); + assert_eq!(vec.get(0), 42u32); +} + +#[test] +fn test_from_single_item_biguint() { + let vec = ManagedVec::>::from_single_item(BigUint::from(42u64)); + assert_eq!(vec.len(), 1); + assert_eq!(*vec.get(0), BigUint::from(42u64)); +} + +#[test] +fn test_clear_u32() { + let mut vec = ManagedVec::::new(); + for i in 1u32..=5u32 { + vec.push(i); + } + assert_eq!(vec.len(), 5); + vec.clear(); + assert!(vec.is_empty()); + // can still push after clear + vec.push(1u32); + assert_eq!(vec.len(), 1); +} + +#[test] +fn test_clear_biguint() { + let mut vec = ManagedVec::>::new(); + for i in 1u64..=5u64 { + vec.push(BigUint::from(i)); + } + assert_eq!(vec.len(), 5); + vec.clear(); + assert!(vec.is_empty()); + // can still push after clear + vec.push(BigUint::from(1u64)); + assert_eq!(vec.len(), 1); +} + +#[test] +fn test_find() { + let mut vec = ManagedVec::::new(); + vec.push(10u32); + vec.push(20u32); + vec.push(30u32); + vec.push(20u32); // duplicate + + assert_eq!(vec.find(&10u32), Some(0)); + assert_eq!(vec.find(&20u32), Some(1)); // first occurrence + assert_eq!(vec.find(&30u32), Some(2)); + assert_eq!(vec.find(&99u32), None); + + let empty = ManagedVec::::new(); + assert_eq!(empty.find(&10u32), None); +} + +#[test] +fn test_contains() { + let mut vec = ManagedVec::::new(); + vec.push(10u32); + vec.push(20u32); + vec.push(30u32); + + assert!(vec.contains(&10u32)); + assert!(vec.contains(&20u32)); + assert!(vec.contains(&30u32)); + assert!(!vec.contains(&99u32)); + + let empty = ManagedVec::::new(); + assert!(!empty.contains(&10u32)); +} diff --git a/tools/managed-mem-bench/src/main.rs b/tools/managed-mem-bench/src/main.rs index fe5f18e5c6..9949fc46d1 100644 --- a/tools/managed-mem-bench/src/main.rs +++ b/tools/managed-mem-bench/src/main.rs @@ -16,11 +16,8 @@ use std::{ sync::atomic::{AtomicI64, Ordering}, }; -use multiversx_sc::types::{ - BigFloat, BigInt, BigUint, EgldDecimals, EgldOrEsdtTokenIdentifier, EsdtTokenPayment, - ManagedAddress, ManagedBuffer, ManagedByteArray, ManagedDecimal, ManagedMap, ManagedVec, - ManagedVecItem, NumDecimals, TokenIdentifier, -}; +use multiversx_sc::derive_imports::*; +use multiversx_sc::imports::*; use multiversx_sc_scenario::api::StaticApi; /// Global allocator wrapper that tracks net allocated heap bytes. @@ -178,6 +175,16 @@ fn main() { ) }); + bench_type("TokenId (native EGLD-000000)", TokenId::::native); + + bench_type("TokenId (ESDT)", || { + TokenId::::from("MYTOKEN-123456") + }); + + bench_type("Payment (fungible ESDT)", || { + Payment::::new("MYTOKEN-123456", 0, NonZeroBigUint::one()) + }); + bench_type("ManagedMap (1 entry)", || { let key = ManagedBuffer::::new_from_bytes(b"key"); let val = ManagedBuffer::::new_from_bytes(&data); @@ -186,6 +193,15 @@ fn main() { m }); + bench_type("EnumWithFields", || EnumWithFields::Variant1(42u32)); + + bench_type("ManagedStructWithBigUint", || ManagedStructWithBigUint::< + StaticApi, + > { + big_uint: BigUint::from(42u64), + num: 42u32, + }); + // ------------------------------------------------------------------------- // ManagedVec of managed types // ------------------------------------------------------------------------- @@ -235,5 +251,104 @@ fn main() { ) }); + bench_managed_vec("ManagedVec (= PaymentVec)", || { + Payment::::new("MYTOKEN-123456", 0, NonZeroBigUint::one()) + }); + + bench_managed_vec("ManagedVec", || 42u8); + bench_managed_vec("ManagedVec", || 42u16); + bench_managed_vec("ManagedVec", || 42u32); + bench_managed_vec("ManagedVec", || 42u64); + bench_managed_vec("ManagedVec", || 42i32); + bench_managed_vec("ManagedVec", || 42i64); + bench_managed_vec("ManagedVec", || 42usize); + bench_managed_vec("ManagedVec", || true); + bench_managed_vec("ManagedVec>", || Some(42i32)); + bench_managed_vec("ManagedVec>", || { + Some(ManagedBuffer::::new_from_bytes(&data)) + }); + + bench_managed_vec("ManagedVec", || { + EnumWithFields::Variant1(42u32) + }); + + bench_managed_vec("ManagedVec", || { + ManagedStructWithBigUint:: { + big_uint: BigUint::from(42u64), + num: 42u32, + } + }); + + bench_managed_vec("ManagedVec>", || { + Some(EnumWithFields::Variant1(42u32)) + }); + + bench_managed_vec("ManagedVec>", || { + Some(ManagedStructWithBigUint:: { + big_uint: BigUint::from(42u64), + num: 42u32, + }) + }); + + bench_managed_vec("ManagedVec>", || { + let mut inner = ManagedVec::::new(); + inner.push(EnumWithFields::Variant1(42u32)); + inner + }); + + bench_managed_vec("ManagedVec>", || { + let mut inner = ManagedVec::>::new(); + inner.push(ManagedStructWithBigUint:: { + big_uint: BigUint::from(42u64), + num: 42u32, + }); + inner + }); + + // ------------------------------------------------------------------------- + // Tx objects (contract call style, mirroring adder tests) + // ------------------------------------------------------------------------- + println!("\n=== Tx objects - contract call style ({NUM_ITEMS} instances each) ===\n"); + println!( + " {:<45} {:>16} {:>14} {:>12}", + "type", "create (bytes)", "hold (bytes)", "residual" + ); + println!(" {}", "-".repeat(95)); + + bench_type("Tx", || { + Tx::new_tx_from_sc() + .from(ManagedAddress::::zero()) + .to(ManagedAddress::::zero()) + .raw_call("bench") + .argument(&42u64) + .argument(&BigUint::::from(42u64)) + .payment(Payment::try_new(TestTokenId::EGLD_000000, 0, 100u32).unwrap()) + .payment( + Payment::try_new( + TokenId::::from("MYTOKEN-123456"), + 0, + BigUint::::from(200u64), + ) + .unwrap(), + ) + }); + println!(); } + +#[derive( + ManagedVecItem, NestedEncode, NestedDecode, TopEncode, TopDecode, PartialEq, Eq, Clone, Debug, +)] +enum EnumWithFields { + Variant1(u32), + Variant2, + Variant3(i64), +} + +#[derive( + ManagedVecItem, NestedEncode, NestedDecode, TopEncode, TopDecode, PartialEq, Eq, Clone, Debug, +)] +pub struct ManagedStructWithBigUint { + pub big_uint: multiversx_sc::types::BigUint, + pub num: u32, +}