diff --git a/uint/Cargo.toml b/uint/Cargo.toml index 0e8171213..3273b9016 100644 --- a/uint/Cargo.toml +++ b/uint/Cargo.toml @@ -4,7 +4,7 @@ homepage = "http://parity.io" repository = "https://github.com/paritytech/parity-common" license = "MIT/Apache-2.0" name = "uint" -version = "0.7.0" +version = "0.7.1" authors = ["Parity Technologies "] readme = "README.md" diff --git a/uint/src/uint.rs b/uint/src/uint.rs index ec488f2c3..5b823f587 100644 --- a/uint/src/uint.rs +++ b/uint/src/uint.rs @@ -50,6 +50,26 @@ macro_rules! impl_map_from { } } +#[macro_export] +#[doc(hidden)] +macro_rules! impl_try_from_for_primitive { + ($from:ident, $to:ty) => { + impl $crate::core_::convert::TryFrom<$from> for $to { + type Error = &'static str; + + #[inline] + fn try_from(u: $from) -> Result<$to, &'static str> { + let $from(arr) = u; + if !u.fits_word() || arr[0] > <$to>::max_value() as u64 { + Err(concat!("integer overflow when casting to ", stringify!($to))) + } else { + Ok(arr[0] as $to) + } + } + } + } +} + #[macro_export] #[doc(hidden)] macro_rules! uint_overflowing_binop { @@ -366,6 +386,36 @@ macro_rules! construct_uint { self.low_u128() } } + + impl $crate::core_::convert::TryFrom<$name> for u128 { + type Error = &'static str; + + #[inline] + fn try_from(u: $name) -> Result { + let $name(arr) = u; + for i in 2..$n_words { + if arr[i] != 0 { + return Err("integer overflow when casting to u128"); + } + } + Ok(((arr[1] as u128) << 64) + arr[0] as u128) + } + } + + impl $crate::core_::convert::TryFrom<$name> for i128 { + type Error = &'static str; + + #[inline] + fn try_from(u: $name) -> Result { + let err_str = "integer overflow when casting to i128"; + let i = u128::try_from(u).map_err(|_| err_str)?; + if i > i128::max_value() as u128 { + Err(err_str) + } else { + Ok(i as i128) + } + } + } }; ( @construct $(#[$attr:meta])* $visibility:vis struct $name:ident ( $n_words:tt ); ) => { /// Little-endian large integer type @@ -1130,6 +1180,17 @@ macro_rules! construct_uint { } } + impl_try_from_for_primitive!($name, u8); + impl_try_from_for_primitive!($name, u16); + impl_try_from_for_primitive!($name, u32); + impl_try_from_for_primitive!($name, usize); + impl_try_from_for_primitive!($name, u64); + impl_try_from_for_primitive!($name, i8); + impl_try_from_for_primitive!($name, i16); + impl_try_from_for_primitive!($name, i32); + impl_try_from_for_primitive!($name, isize); + impl_try_from_for_primitive!($name, i64); + impl $crate::core_::ops::Add for $name where T: Into<$name> { type Output = $name; diff --git a/uint/tests/uint_tests.rs b/uint/tests/uint_tests.rs index 54d168ca0..de77b828e 100644 --- a/uint/tests/uint_tests.rs +++ b/uint/tests/uint_tests.rs @@ -12,6 +12,7 @@ extern crate crunchy; use core::u64::MAX; use core::str::FromStr; +use core::convert::TryInto; use uint::{FromDecStrErr}; construct_uint! { @@ -188,6 +189,55 @@ fn uint256_from() { ); } +#[test] +fn uint256_try_into_primitives() { + macro_rules! try_into_uint_primitive_ok { + ($primitive: ty) => { + assert_eq!(U256::from(10).try_into() as Result<$primitive, _>, Ok(<$primitive>::from(10u8))); + } + } + try_into_uint_primitive_ok!(u8); + try_into_uint_primitive_ok!(u16); + try_into_uint_primitive_ok!(u32); + try_into_uint_primitive_ok!(usize); + try_into_uint_primitive_ok!(u64); + try_into_uint_primitive_ok!(u128); + + macro_rules! try_into_iint_primitive_ok { + ($primitive: ty) => { + assert_eq!(U256::from(10).try_into() as Result<$primitive, _>, Ok(<$primitive>::from(10i8))); + } + } + try_into_iint_primitive_ok!(i8); + try_into_iint_primitive_ok!(i16); + try_into_iint_primitive_ok!(i32); + try_into_iint_primitive_ok!(isize); + try_into_iint_primitive_ok!(i64); + try_into_iint_primitive_ok!(i128); + + macro_rules! try_into_primitive_err { + ($small: ty, $big: ty) => { + assert_eq!( + U256::from(<$small>::max_value() as $big + 1).try_into() as Result<$small, _>, + Err(concat!("integer overflow when casting to ", stringify!($small))) + ); + } + } + try_into_primitive_err!(u8, u16); + try_into_primitive_err!(u16, u32); + try_into_primitive_err!(u32, u64); + try_into_primitive_err!(usize, u128); + try_into_primitive_err!(u64, u128); + assert_eq!(U256([0, 0, 1, 0]).try_into() as Result, Err("integer overflow when casting to u128")); + try_into_primitive_err!(i8, i16); + try_into_primitive_err!(i16, i32); + try_into_primitive_err!(i32, i64); + try_into_primitive_err!(isize, i128); + try_into_primitive_err!(i64, i128); + try_into_primitive_err!(i128, u128); + assert_eq!(U256([0, 0, 1, 0]).try_into() as Result, Err("integer overflow when casting to i128")); +} + #[test] fn uint256_to() { let hex = "8090a0b0c0d0e0f00910203040506077583a2cf8264910e1436bda32571012f0";