Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions framework/base/src/err_msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub const CAST_TO_I64_ERROR: &str = "cast to i64 error";
pub const BIG_UINT_EXCEEDS_SLICE: &str = "big uint as_bytes exceed target slice";
pub const BIG_UINT_SUB_NEGATIVE: &str = "cannot subtract because result would be negative";
pub const BIG_UINT_NTH_ROOT_ZERO: &str = "cannot compute 0th root";
pub const BIG_UINT_PARSE_ERROR: &str = "invalid decimal string for BigUint";
pub const UNSIGNED_NEGATIVE: &str = "cannot convert to unsigned, number is negative";
pub const ZERO_VALUE_NOT_ALLOWED: &str = "zero value not allowed";
pub const PROPORTION_OVERFLOW_ERR: &str = "proportion overflow";
Expand Down
2 changes: 1 addition & 1 deletion framework/base/src/types/managed/wrapped/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod non_zero_big_uint;
mod non_zero_big_uint_cmp;
mod non_zero_big_uint_operators;

pub use big_uint::BigUint;
pub use big_uint::{BigUint, ParseBigUintError};
pub use non_zero_big_uint::NonZeroBigUint;

/// Error returned when attempting to convert zero to a non-zero number type.
Expand Down
32 changes: 32 additions & 0 deletions framework/base/src/types/managed/wrapped/num/big_uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,38 @@ impl<M: ManagedTypeApi> BigUint<M> {
}
}

/// Error returned when parsing a `BigUint` from a decimal string fails.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseBigUintError;

impl core::fmt::Display for ParseBigUintError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(err_msg::BIG_UINT_PARSE_ERROR)
}
}

impl<M: ManagedTypeApi> core::str::FromStr for BigUint<M> {
type Err = ParseBigUintError;

/// Parses a decimal string into a `BigUint`.
///
/// Returns `Err(ParseBigUintError)` if the string is empty or contains non-digit characters.
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Err(ParseBigUintError);
}
let mut result = BigUint::zero();
for byte in s.bytes() {
if !byte.is_ascii_digit() {
return Err(ParseBigUintError);
}
result *= 10u64;
result += (byte - b'0') as u64;
}
Ok(result)
}
}

impl<M: ManagedTypeApi> Clone for BigUint<M> {
fn clone(&self) -> Self {
unsafe { self.as_big_int().clone().into_big_uint_unchecked() }
Expand Down
62 changes: 61 additions & 1 deletion framework/scenario/tests/big_uint_test.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use multiversx_sc::types::{BigUint, SaturatingSub, SaturatingSubAssign};
use core::str::FromStr;
use multiversx_sc::types::{BigUint, ParseBigUintError, SaturatingSub, SaturatingSubAssign};
use multiversx_sc_scenario::api::StaticApi;

// BigUint intentionally does not implement Send or Sync,
Expand Down Expand Up @@ -175,3 +176,62 @@ fn test_big_uint_nth_root() {
fn test_big_uint_nth_root_zero_k() {
let _ = BigUint::<StaticApi>::from(10u64).nth_root(0);
}

#[test]
fn test_big_uint_parse() {
let parse = |s: &str| -> u64 { BigUint::<StaticApi>::from_str(s).unwrap().to_u64().unwrap() };

// smaller values
assert_eq!(parse("0"), 0);
assert_eq!(parse("1"), 1);
assert_eq!(parse("42"), 42);
assert_eq!(parse("1000000"), 1_000_000);
assert_eq!(parse("9223372036854775807"), i64::MAX as u64); // largest i64

// larger values
let ten = BigUint::<StaticApi>::from(10u64);
assert_eq!(
"10000000000000000000"
.parse::<BigUint<StaticApi>>()
.unwrap(),
ten.pow(19)
); // 10^19 > i64::MAX
assert_eq!(
"100000000000000000000"
.parse::<BigUint<StaticApi>>()
.unwrap(),
ten.pow(20)
); // 10^20 > u64::MAX
assert_eq!(
"1000000000000000000000000000000"
.parse::<BigUint<StaticApi>>()
.unwrap(),
ten.pow(30)
);

// str::parse uses FromStr, so .parse() should also work
let big: BigUint<StaticApi> = "12345".parse().unwrap();
assert_eq!(big, BigUint::<StaticApi>::from(12345u64));
}

#[test]
fn test_big_uint_parse_errors() {
assert_eq!(BigUint::<StaticApi>::from_str(""), Err(ParseBigUintError));
assert_eq!(
BigUint::<StaticApi>::from_str("abc"),
Err(ParseBigUintError)
);
assert_eq!(
BigUint::<StaticApi>::from_str("12x4"),
Err(ParseBigUintError)
);
assert_eq!(BigUint::<StaticApi>::from_str("-1"), Err(ParseBigUintError));
assert_eq!(
BigUint::<StaticApi>::from_str("1.0"),
Err(ParseBigUintError)
);
assert_eq!(
BigUint::<StaticApi>::from_str("123e4"),
Err(ParseBigUintError)
);
}
Loading