Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uint: add casting from/to U64 #691

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
35 changes: 13 additions & 22 deletions ethereum-types/src/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[cfg(feature = "codec")]
use impl_codec::impl_uint_codec;
#[cfg(feature = "rlp")]
use impl_rlp::impl_uint_rlp;
#[cfg(feature = "serialize")]
use impl_serde::impl_uint_serde;
use uint_crate::*;

pub use uint_crate::{FromDecStrErr, FromStrRadixErr, FromStrRadixErrKind};

construct_uint! {
/// Unsigned 64-bit integer.
pub struct U64(1);
}
#[cfg(feature = "rlp")]
impl_uint_rlp!(U64, 1);
#[cfg(feature = "serialize")]
impl_uint_serde!(U64, 1);
#[cfg(feature = "codec")]
impl_uint_codec!(U64, 1);

pub use primitive_types::{U128, U256, U512};
pub use primitive_types::{U64, U128, U256, U512};

#[cfg(test)]
mod tests {
use super::{U256, U512};
use super::{U64, U256, U512};
use serde_json as ser;
use std::u64::MAX;

Expand Down Expand Up @@ -99,6 +79,17 @@ mod tests {
.is_data());
}

#[test]
fn test_serialize_u64() {
assert_eq!(
ser::to_string_pretty(&!U64::zero()).unwrap(),
"\"0xffffffffffffffff\""
);
assert!(ser::from_str::<U64>("\"0x1ffffffffffffffff\"")
.unwrap_err()
.is_data());
}

#[test]
fn fixed_arrays_roundtrip() {
let raw: U256 = "7094875209347850239487502394881".into();
Expand Down
72 changes: 72 additions & 0 deletions primitive-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ pub enum Error {
Overflow,
}

construct_uint! {
/// 64-bit unsigned integer.
#[cfg_attr(feature = "scale-info", derive(TypeInfo))]
pub struct U64(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not keen on adding this type to primitives.
In fact, we wanted to remove it altogether, see #473 for the context.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad for not reading this issue thoroughly, and thanks for pointing it out.

I understand now why you suggested adding this to ethereum-types a year ago in #473. Just for clarity, you are still okay with adding this functionality in general, right?

My motivation behind this is that there are still libraries that use U64 in certain cases (eg. ethers-rs block-num) and conversion to other uint types is currently not the most elegant as already pointed out in the referenced issue.

}
construct_uint! {
/// 128-bit unsigned integer.
#[cfg_attr(feature = "scale-info", derive(TypeInfo))]
Expand Down Expand Up @@ -83,6 +88,7 @@ mod num_traits {
use super::*;
use impl_num_traits::impl_uint_num_traits;

impl_uint_num_traits!(U64, 1);
impl_uint_num_traits!(U128, 2);
impl_uint_num_traits!(U256, 4);
impl_uint_num_traits!(U512, 8);
Expand All @@ -93,6 +99,7 @@ mod serde {
use super::*;
use impl_serde::{impl_fixed_hash_serde, impl_uint_serde};

impl_uint_serde!(U64, 1);
impl_uint_serde!(U128, 2);
impl_uint_serde!(U256, 4);
impl_uint_serde!(U512, 8);
Expand All @@ -110,6 +117,7 @@ mod codec {
use super::*;
use impl_codec::{impl_fixed_hash_codec, impl_uint_codec};

impl_uint_codec!(U64, 1);
impl_uint_codec!(U128, 2);
impl_uint_codec!(U256, 4);
impl_uint_codec!(U512, 8);
Expand All @@ -127,6 +135,7 @@ mod rlp {
use super::*;
use impl_rlp::{impl_fixed_hash_rlp, impl_uint_rlp};

impl_uint_rlp!(U64, 1);
impl_uint_rlp!(U128, 2);
impl_uint_rlp!(U256, 4);
impl_uint_rlp!(U512, 8);
Expand All @@ -141,6 +150,15 @@ mod rlp {

impl_fixed_hash_conversions!(H256, H160);

impl U64 {
/// Multiplies two 64-bit integers to produce full 128-bit integer.
/// Overflow is not possible.
#[inline(always)]
pub fn full_mul(self, other: Self) -> U128 {
U128(uint_full_mul_reg!(Self, 1, self, other))
}
}

impl U128 {
/// Multiplies two 128-bit integers to produce full 256-bit integer.
/// Overflow is not possible.
Expand All @@ -159,6 +177,24 @@ impl U256 {
}
}

impl From<U64> for U128 {
fn from(value: U64) -> Self {
Self([value.0[0], 0])
}
}

impl From<U64> for U256 {
fn from(value: U64) -> Self {
Self([value.0[0], 0, 0, 0])
}
}

impl From<U64> for U512 {
fn from(value: U64) -> Self {
Self([value.0[0], 0, 0, 0, 0, 0, 0, 0])
}
}

impl From<U256> for U512 {
fn from(value: U256) -> U512 {
let U256(ref arr) = value;
Expand All @@ -171,6 +207,42 @@ impl From<U256> for U512 {
}
}

impl TryFrom<U128> for U64 {
type Error = Error;

fn try_from(value: U128) -> Result<Self, Error> {
let U128(ref arr) = value;
if arr[1] != 0 {
return Err(Error::Overflow)
}
Ok(Self([arr[0]]))
}
}

impl TryFrom<U256> for U64 {
type Error = Error;

fn try_from(value: U256) -> Result<Self, Error> {
let U256(ref arr) = value;
if arr[1] | arr[2]| arr[3] != 0 {
return Err(Error::Overflow)
}
Ok(Self([arr[0]]))
}
}

impl TryFrom<U512> for U64 {
type Error = Error;

fn try_from(value: U512) -> Result<Self, Error> {
let U512(ref arr) = value;
if arr[1] | arr[2]| arr[3]| arr[4] | arr[5] | arr[6] | arr[7] != 0 {
return Err(Error::Overflow)
}
Ok(Self([arr[0]]))
}
}

impl TryFrom<U256> for U128 {
type Error = Error;

Expand Down
44 changes: 44 additions & 0 deletions primitive-types/tests/cast.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use primitive_types::{Error, U128, U256, U512, U64};


#[test]
#[allow(non_snake_case)]
fn from_U64() {
let a = U64::from(222);

assert_eq!(U128::from(a), U128::from(222), "U64 -> U128");
assert_eq!(U256::from(a), U256::from(222), "U64 -> U256");
assert_eq!(U512::from(a), U512::from(222), "U64 -> U512");
}

#[test]
#[allow(non_snake_case)]
fn to_U64() {
// U128 -> U64
assert_eq!(U64::try_from(U128([222, 0])), Ok(U64::from(222)), "U128 -> U64");
assert_eq!(U64::try_from(U128([222, 1])), Err(Error::Overflow), "U128 -> U64 :: Overflow");

// U256 -> U64
assert_eq!(U64::try_from(U256([222, 0, 0, 0])), Ok(U64::from(222)), "U256 -> U64");
for i in 1..4 {
let mut arr = [222, 0, 0, 0];
arr[i] = 1;
assert_eq!(U64::try_from(U256(arr)), Err(Error::Overflow), "U256 -> U64 :: Overflow");
}

// U512 -> U64
assert_eq!(U64::try_from(U512([222, 0, 0, 0, 0, 0, 0, 0])),Ok(U64::from(222)), "U512 -> U64");
for i in 1..8 {
let mut arr = [222, 0, 0, 0, 0, 0, 0, 0];
arr[i] = 1;
assert_eq!(U64::try_from(U512(arr)), Err(Error::Overflow), "U512 -> U64");
}
}

#[test]
#[allow(non_snake_case)]
fn full_mul_U64() {
let a = U64::MAX;
let b = U64::from(2);
assert_eq!(a.full_mul(b), U128::from(a)*U128::from(b))
}
9 changes: 8 additions & 1 deletion primitive-types/tests/num_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@
// except according to those terms.

use impl_num_traits::integer_sqrt::IntegerSquareRoot;
use primitive_types::U256;
use primitive_types::{U64, U256};

#[test]
fn u256_isqrt() {
let x = U256::MAX;
let s = x.integer_sqrt_checked().unwrap();
assert_eq!(x.integer_sqrt(), s);
}

#[test]
fn u64_isqrt() {
let x = U64::MAX;
let s = x.integer_sqrt_checked().unwrap();
assert_eq!(x.integer_sqrt(), s);
}