Skip to main content

multiversx_sc/types/managed/basic/
big_int.rs

1use core::{convert::TryInto, marker::PhantomData};
2
3use crate::{
4    abi::{TypeAbiFrom, TypeName},
5    api::{
6        BigIntApiImpl, HandleConstraints, ManagedBufferApiImpl, ManagedTypeApi, ManagedTypeApiImpl,
7        RawHandle, StaticVarApiImpl, const_handles, use_raw_handle,
8    },
9    codec::{
10        DecodeErrorHandler, EncodeErrorHandler, NestedDecode, NestedDecodeInput, NestedEncode,
11        NestedEncodeOutput, TopDecode, TopDecodeInput, TopEncode, TopEncodeOutput, TryStaticCast,
12    },
13    formatter::{FormatByteReceiver, SCDisplay, hex_util::encode_bytes_as_hex},
14    types::{
15        BigUint, ManagedBuffer, ManagedOption, ManagedRef, ManagedType, Sign, heap::BoxedBytes,
16    },
17};
18
19use super::cast_to_i64::cast_to_i64;
20
21#[repr(transparent)]
22pub struct BigInt<M: ManagedTypeApi> {
23    pub(crate) handle: M::BigIntHandle,
24    _phantom: PhantomData<M>,
25}
26
27impl<M: ManagedTypeApi> ManagedType<M> for BigInt<M> {
28    type OwnHandle = M::BigIntHandle;
29
30    unsafe fn from_handle(handle: M::BigIntHandle) -> Self {
31        BigInt {
32            handle,
33            _phantom: PhantomData,
34        }
35    }
36
37    fn get_handle(&self) -> M::BigIntHandle {
38        self.handle.clone()
39    }
40
41    unsafe fn forget_into_handle(self) -> Self::OwnHandle {
42        unsafe {
43            let handle = core::ptr::read(&self.handle);
44            core::mem::forget(self);
45            handle
46        }
47    }
48
49    fn transmute_from_handle_ref(handle_ref: &M::BigIntHandle) -> &Self {
50        unsafe { core::mem::transmute(handle_ref) }
51    }
52
53    fn transmute_from_handle_ref_mut(handle_ref: &mut M::BigIntHandle) -> &mut Self {
54        unsafe { core::mem::transmute(handle_ref) }
55    }
56}
57
58impl<M: ManagedTypeApi> Default for BigInt<M> {
59    #[inline]
60    fn default() -> Self {
61        Self::zero()
62    }
63}
64
65impl<M: ManagedTypeApi> From<&ManagedBuffer<M>> for BigInt<M> {
66    #[inline]
67    fn from(item: &ManagedBuffer<M>) -> Self {
68        BigInt::from_signed_bytes_be_buffer(item)
69    }
70}
71
72impl<M: ManagedTypeApi> From<ManagedBuffer<M>> for BigInt<M> {
73    #[inline]
74    fn from(item: ManagedBuffer<M>) -> Self {
75        BigInt::from_signed_bytes_be_buffer(&item)
76    }
77}
78
79impl<M: ManagedTypeApi> BigInt<M> {
80    /// Creates a new object, without initializing it.
81    ///
82    /// ## Safety
83    ///
84    /// The value needs to be initialized after creation, otherwise the VM will halt the first time the value is attempted to be read.
85    pub unsafe fn new_uninit() -> Self {
86        unsafe {
87            let new_handle: M::BigIntHandle =
88                use_raw_handle(M::static_var_api_impl().next_handle());
89            BigInt::from_handle(new_handle)
90        }
91    }
92
93    pub(crate) fn set_value<T>(handle: M::BigIntHandle, value: T)
94    where
95        T: TryInto<i64>,
96    {
97        M::managed_type_impl().bi_set_int64(handle, cast_to_i64::<M, _>(value));
98    }
99
100    pub(crate) fn make_temp<T>(handle: RawHandle, value: T) -> M::BigIntHandle
101    where
102        T: TryInto<i64>,
103    {
104        let temp: M::BigIntHandle = use_raw_handle(handle);
105        Self::set_value(temp.clone(), value);
106        temp
107    }
108}
109
110impl<M: ManagedTypeApi> From<BigUint<M>> for BigInt<M> {
111    #[inline]
112    fn from(item: BigUint<M>) -> Self {
113        item.into_big_int()
114    }
115}
116
117macro_rules! big_int_conv_num {
118    ($num_ty:ty) => {
119        impl<M: ManagedTypeApi> From<$num_ty> for BigInt<M> {
120            #[inline]
121            fn from(value: $num_ty) -> Self {
122                unsafe {
123                    let result = BigInt::new_uninit();
124                    Self::set_value(result.get_handle(), value);
125                    result
126                }
127            }
128        }
129
130        impl<M: ManagedTypeApi> TypeAbiFrom<$num_ty> for BigInt<M> {}
131        impl<M: ManagedTypeApi> TypeAbiFrom<&$num_ty> for BigInt<M> {}
132    };
133}
134
135// TODO: more coverage, only from i64 currently tested
136big_int_conv_num! {i64}
137big_int_conv_num! {i32}
138big_int_conv_num! {isize}
139big_int_conv_num! {i16}
140big_int_conv_num! {i8}
141
142#[cfg(feature = "num-bigint")]
143impl<M: ManagedTypeApi> TypeAbiFrom<crate::codec::num_bigint::BigInt> for BigInt<M> {}
144#[cfg(feature = "num-bigint")]
145impl<M: ManagedTypeApi> TypeAbiFrom<BigInt<M>> for crate::codec::num_bigint::BigInt {}
146
147impl<M> TypeAbiFrom<Self> for BigInt<M> where M: ManagedTypeApi {}
148impl<M> TypeAbiFrom<&Self> for BigInt<M> where M: ManagedTypeApi {}
149
150impl<M: ManagedTypeApi> crate::abi::TypeAbi for BigInt<M> {
151    #[cfg(feature = "num-bigint")]
152    type Unmanaged = crate::codec::num_bigint::BigInt;
153
154    #[cfg(not(feature = "num-bigint"))]
155    type Unmanaged = Self;
156
157    fn type_name() -> TypeName {
158        TypeName::from("BigInt")
159    }
160
161    fn type_name_rust() -> TypeName {
162        TypeName::from("BigInt<$API>")
163    }
164}
165
166#[cfg(feature = "num-bigint")]
167impl<M: ManagedTypeApi> From<&crate::codec::num_bigint::BigInt> for BigInt<M> {
168    fn from(alloc_big_int: &crate::codec::num_bigint::BigInt) -> Self {
169        BigInt::from_signed_bytes_be(alloc_big_int.to_signed_bytes_be().as_slice())
170    }
171}
172#[cfg(feature = "num-bigint")]
173impl<M: ManagedTypeApi> From<crate::codec::num_bigint::BigInt> for BigInt<M> {
174    fn from(alloc_big_int: crate::codec::num_bigint::BigInt) -> Self {
175        BigInt::from(&alloc_big_int)
176    }
177}
178
179impl<M: ManagedTypeApi> BigInt<M> {
180    #[inline]
181    pub fn zero() -> Self {
182        unsafe {
183            let result = BigInt::new_uninit();
184            M::managed_type_impl().bi_set_int64(result.get_handle(), 0);
185            result
186        }
187    }
188
189    #[inline]
190    pub fn to_i64(&self) -> Option<i64> {
191        M::managed_type_impl().bi_to_i64(self.handle.clone())
192    }
193
194    #[inline]
195    pub fn overwrite_i64(&mut self, value: i64) {
196        Self::set_value(self.handle.clone(), value);
197    }
198
199    #[inline]
200    pub fn from_signed_bytes_be(bytes: &[u8]) -> Self {
201        let mb_handle: M::ManagedBufferHandle = use_raw_handle(const_handles::MBUF_TEMPORARY_1);
202        M::managed_type_impl().mb_overwrite(mb_handle.clone(), bytes);
203        unsafe {
204            let result = BigInt::new_uninit();
205            M::managed_type_impl().mb_to_big_int_signed(mb_handle, result.get_handle());
206            result
207        }
208    }
209
210    #[inline]
211    pub fn to_signed_bytes_be(&self) -> BoxedBytes {
212        let mb_handle: M::ManagedBufferHandle = use_raw_handle(const_handles::MBUF_TEMPORARY_1);
213        M::managed_type_impl().mb_from_big_int_signed(self.handle.clone(), mb_handle.clone());
214        M::managed_type_impl().mb_to_boxed_bytes(mb_handle)
215    }
216
217    #[inline]
218    pub fn from_signed_bytes_be_buffer(managed_buffer: &ManagedBuffer<M>) -> Self {
219        unsafe {
220            let result = BigInt::new_uninit();
221            M::managed_type_impl()
222                .mb_to_big_int_signed(managed_buffer.handle.clone(), result.get_handle());
223            result
224        }
225    }
226
227    #[inline]
228    pub fn to_signed_bytes_be_buffer(&self) -> ManagedBuffer<M> {
229        unsafe {
230            let result = ManagedBuffer::new_uninit();
231            M::managed_type_impl().mb_from_big_int_signed(self.handle.clone(), result.get_handle());
232            result
233        }
234    }
235
236    pub(crate) fn clone_to_handle(source_handle: M::BigIntHandle, dest_handle: M::BigIntHandle) {
237        let api = M::managed_type_impl();
238        api.bi_set_int64(dest_handle.clone(), 0);
239        api.bi_add(dest_handle.clone(), dest_handle, source_handle);
240    }
241}
242
243impl<M: ManagedTypeApi> Clone for BigInt<M> {
244    fn clone(&self) -> Self {
245        unsafe {
246            let result = BigInt::new_uninit();
247            BigInt::<M>::clone_to_handle(self.get_handle(), result.get_handle());
248            result
249        }
250    }
251}
252
253impl<M: ManagedTypeApi> BigInt<M> {
254    pub fn from_biguint(sign: Sign, unsigned: BigUint<M>) -> Self {
255        let result = unsigned.into_big_int();
256        if sign.is_minus() {
257            M::managed_type_impl().bi_neg(result.handle.clone(), result.handle.clone());
258        }
259        result
260    }
261
262    /// Returns the sign of the `BigInt` as a `Sign`.
263    pub fn sign(&self) -> Sign {
264        let api = M::managed_type_impl();
265        match api.bi_sign(self.handle.clone()) {
266            crate::api::Sign::Plus => Sign::Plus,
267            crate::api::Sign::NoSign => Sign::NoSign,
268            crate::api::Sign::Minus => Sign::Minus,
269        }
270    }
271
272    /// Returns the magnitude of the `BigInt` as a `BigUint`.
273    pub fn magnitude(&self) -> BigUint<M> {
274        unsafe {
275            let result = BigUint::new_uninit();
276            M::managed_type_impl().bi_abs(result.get_handle(), self.get_handle());
277            result
278        }
279    }
280
281    /// Convert this `BigInt` into its `Sign` and `BigUint` magnitude,
282    /// the reverse of `BigInt::from_biguint`.
283    pub fn to_parts(self) -> (Sign, BigUint<M>) {
284        (self.sign(), self.magnitude())
285    }
286
287    /// Converts to an unsigned `BigUint`, without performing any checks.
288    ///
289    /// # Safety
290    ///
291    /// If the number is negative, undefined behavior might occur further down the execution.
292    pub unsafe fn into_big_uint_unchecked(self) -> BigUint<M> {
293        BigUint { value: self }
294    }
295
296    /// Converts this `BigInt` into a `BigUint`, if it's not negative.
297    pub fn into_big_uint(self) -> ManagedOption<M, BigUint<M>> {
298        if let Sign::Minus = self.sign() {
299            ManagedOption::none()
300        } else {
301            ManagedOption::some(unsafe { self.into_big_uint_unchecked() })
302        }
303    }
304}
305
306impl<M: ManagedTypeApi> TryStaticCast for BigInt<M> {}
307
308impl<M: ManagedTypeApi> TopEncode for BigInt<M> {
309    #[inline]
310    fn top_encode_or_handle_err<O, H>(&self, output: O, h: H) -> Result<(), H::HandledErr>
311    where
312        O: TopEncodeOutput,
313        H: EncodeErrorHandler,
314    {
315        if O::supports_specialized_type::<Self>() {
316            output.set_specialized(self, h)
317        } else {
318            output.set_slice_u8(self.to_signed_bytes_be().as_slice());
319            Ok(())
320        }
321    }
322}
323
324impl<M: ManagedTypeApi> NestedEncode for BigInt<M> {
325    fn dep_encode_or_handle_err<O, H>(&self, dest: &mut O, h: H) -> Result<(), H::HandledErr>
326    where
327        O: NestedEncodeOutput,
328        H: EncodeErrorHandler,
329    {
330        self.to_signed_bytes_be_buffer()
331            .dep_encode_or_handle_err(dest, h)
332    }
333}
334
335impl<M: ManagedTypeApi> NestedDecode for BigInt<M> {
336    fn dep_decode_or_handle_err<I, H>(input: &mut I, h: H) -> Result<Self, H::HandledErr>
337    where
338        I: NestedDecodeInput,
339        H: DecodeErrorHandler,
340    {
341        if I::supports_specialized_type::<Self>() {
342            input.read_specialized((), h)
343        } else {
344            let boxed_bytes = BoxedBytes::dep_decode_or_handle_err(input, h)?;
345            Ok(Self::from_signed_bytes_be(boxed_bytes.as_slice()))
346        }
347    }
348}
349
350impl<M: ManagedTypeApi> TopDecode for BigInt<M> {
351    fn top_decode_or_handle_err<I, H>(input: I, h: H) -> Result<Self, H::HandledErr>
352    where
353        I: TopDecodeInput,
354        H: DecodeErrorHandler,
355    {
356        if I::supports_specialized_type::<Self>() {
357            input.into_specialized(h)
358        } else {
359            let boxed_bytes = BoxedBytes::top_decode_or_handle_err(input, h)?;
360            Ok(Self::from_signed_bytes_be(boxed_bytes.as_slice()))
361        }
362    }
363}
364
365impl<M: ManagedTypeApi> BigInt<M> {
366    #[must_use]
367    pub fn pow(&self, exp: u32) -> Self {
368        let exp_handle = BigUint::<M>::make_temp(const_handles::BIG_INT_TEMPORARY_1, exp);
369        unsafe {
370            let result = BigInt::new_uninit();
371            M::managed_type_impl().bi_pow(result.get_handle(), self.get_handle(), exp_handle);
372            result
373        }
374    }
375
376    /// Calculates proportion of this value, consuming self.
377    ///
378    /// # Arguments
379    /// * `part` - The numerator value (can be negative)
380    /// * `total` - The denominator value for the ratio calculation
381    ///
382    /// # Returns
383    /// The proportional amount as BigInt (self * part / total)
384    pub fn into_proportion(mut self, part: i64, total: i64) -> Self {
385        let mut temp = BigInt::from(part);
386        self *= &temp;
387        temp.overwrite_i64(total);
388        self /= &temp;
389        self
390    }
391
392    /// Calculates proportion of this value.
393    ///
394    /// # Arguments
395    /// * `part` - The numerator value (can be negative)
396    /// * `total` - The denominator value for the ratio calculation
397    ///
398    /// # Returns
399    /// The proportional amount as BigInt (self * part / total)
400    pub fn proportion(&self, part: i64, total: i64) -> Self {
401        self.clone().into_proportion(part, total)
402    }
403
404    /// Creates a managed buffer containing the textual representation of the number.
405    pub fn to_display(&self) -> ManagedBuffer<M> {
406        unsafe {
407            let result = ManagedBuffer::<M>::new_uninit();
408            M::managed_type_impl().bi_to_string(self.handle.clone(), result.handle.clone());
409            result
410        }
411    }
412}
413
414impl<M: ManagedTypeApi> SCDisplay for BigInt<M> {
415    fn fmt<F: FormatByteReceiver>(&self, f: &mut F) {
416        let str_handle: M::ManagedBufferHandle = use_raw_handle(const_handles::MBUF_TEMPORARY_1);
417        M::managed_type_impl().bi_to_string(self.handle.clone(), str_handle.clone());
418        let cast_handle = str_handle.cast_or_signal_error::<M, _>();
419        let wrap_cast = unsafe { ManagedRef::wrap_handle(cast_handle) };
420        f.append_managed_buffer(&wrap_cast);
421    }
422}
423
424#[cfg(feature = "alloc")]
425impl<M: ManagedTypeApi> core::fmt::Display for BigInt<M> {
426    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
427        core::fmt::Display::fmt(&self.to_display(), f)
428    }
429}
430
431impl<M: ManagedTypeApi> core::fmt::Debug for BigInt<M> {
432    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
433        f.debug_struct("BigInt")
434            .field("handle", &self.handle.clone())
435            .field(
436                "hex-value-be",
437                &encode_bytes_as_hex(self.to_signed_bytes_be().as_slice()),
438            )
439            .finish()
440    }
441}