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

Implement Num from num-traits #480

Merged
merged 1 commit into from Dec 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions ethereum-types/Cargo.toml
Expand Up @@ -26,3 +26,4 @@ serialize = ["std", "impl-serde", "primitive-types/serde", "ethbloom/serialize"]
arbitrary = ["ethbloom/arbitrary", "fixed-hash/arbitrary", "uint-crate/arbitrary"]
rlp = ["impl-rlp", "ethbloom/rlp", "primitive-types/rlp"]
codec = ["impl-codec", "ethbloom/codec"]
num-traits = ["primitive-types/num-traits"]
2 changes: 2 additions & 0 deletions ethereum-types/src/lib.rs
Expand Up @@ -13,6 +13,8 @@ mod uint;

pub use ethbloom::{Bloom, BloomRef, Input as BloomInput};
pub use hash::{BigEndianHash, H128, H160, H256, H264, H32, H512, H520, H64};
#[cfg(feature = "num-traits")]
pub use primitive_types::{FromStrRadixErr, FromStrRadixErrKind};
pub use uint::{FromDecStrErr, U128, U256, U512, U64};

pub type Address = H160;
Expand Down
2 changes: 2 additions & 0 deletions primitive-types/Cargo.toml
Expand Up @@ -12,6 +12,7 @@ fixed-hash = { version = "0.6", path = "../fixed-hash", default-features = false
uint = { version = "0.8.3", path = "../uint", default-features = false }
impl-serde = { version = "0.3.1", path = "impls/serde", default-features = false, optional = true }
impl-codec = { version = "0.4.1", path = "impls/codec", default-features = false, optional = true }
impl-num-traits = { version = "0.1.0", path = "impls/num-traits", default-features = false, optional = true }
impl-rlp = { version = "0.3", path = "impls/rlp", default-features = false, optional = true }
scale-info = { version = "0.4", features = ["derive"], default-features = false, optional = true }

Expand All @@ -26,6 +27,7 @@ codec = ["impl-codec"]
rlp = ["impl-rlp"]
arbitrary = ["fixed-hash/arbitrary", "uint/arbitrary"]
fp-conversion = ["std"]
num-traits = ["impl-num-traits"]

[[test]]
name = "scale_info"
Expand Down
16 changes: 16 additions & 0 deletions primitive-types/impls/num-traits/Cargo.toml
@@ -0,0 +1,16 @@
[package]
name = "impl-num-traits"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
license = "MIT OR Apache-2.0"
homepage = "https://github.com/paritytech/parity-common"
description = "num-traits implementation for uint."
edition = "2018"

[dependencies]
num-traits = { version = "0.2", default-features = false }
uint = { version = "0.8.5", path = "../../../uint", default-features = false }

[features]
default = ["std"]
std = ["num-traits/std", "uint/std"]
49 changes: 49 additions & 0 deletions primitive-types/impls/num-traits/src/lib.rs
@@ -0,0 +1,49 @@
// Copyright 2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! num-traits support for uint.

#![cfg_attr(not(feature = "std"), no_std)]
Copy link
Member

@niklasad1 niklasad1 Dec 23, 2020

Choose a reason for hiding this comment

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

I don't understand why this is needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Bit of a leftover from moving from_str_radix into the uint crate. I don't believe this crate needs it ever, but there is a feature that enables std in uint and num-traits.


#[doc(hidden)]
pub use num_traits;

pub use uint::{FromStrRadixErr, FromStrRadixErrKind};

/// Add num-traits support to an integer created by `construct_uint!`.
#[macro_export]
macro_rules! impl_uint_num_traits {
($name: ident, $len: expr) => {
impl $crate::num_traits::identities::Zero for $name {
#[inline]
fn zero() -> Self {
Self::zero()
}

#[inline]
fn is_zero(&self) -> bool {
self.is_zero()
}
}

impl $crate::num_traits::identities::One for $name {
#[inline]
fn one() -> Self {
Self::one()
}
}

impl $crate::num_traits::Num for $name {
type FromStrRadixErr = $crate::FromStrRadixErr;

fn from_str_radix(txt: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
Self::from_str_radix(txt, radix)
}
}
};
}
11 changes: 11 additions & 0 deletions primitive-types/src/lib.rs
Expand Up @@ -22,6 +22,7 @@ use fixed_hash::{construct_fixed_hash, impl_fixed_hash_conversions};
#[cfg(feature = "scale-info")]
use scale_info::TypeInfo;
use uint::{construct_uint, uint_full_mul_reg};
pub use uint::{FromStrRadixErr, FromStrRadixErrKind};
Copy link
Member

Choose a reason for hiding this comment

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

hmm, I overlooked this line, seems redundant
we could return uint::FromStrRadixErr in impls/num-traits instead of $crate::...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not super familiar with the macro hygiene rules, but what if the downstream crate did a extern crate uint as floop?

Copy link
Member

Choose a reason for hiding this comment

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

yeah, fair point, we can do what impl/rlp does, but still this like seems redundant


/// Error type for conversion.
#[derive(Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -68,6 +69,16 @@ construct_fixed_hash! {
pub struct H512(64);
}

#[cfg(feature = "num-traits")]
mod num_traits {
use super::*;
use impl_num_traits::impl_uint_num_traits;

impl_uint_num_traits!(U128, 2);
impl_uint_num_traits!(U256, 4);
impl_uint_num_traits!(U512, 8);
}

#[cfg(feature = "impl-serde")]
mod serde {
use super::*;
Expand Down
108 changes: 108 additions & 0 deletions uint/src/uint.rs
Expand Up @@ -31,6 +31,102 @@

use core::fmt;

/// A list of error categories encountered when parsing numbers.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
#[non_exhaustive]
pub enum FromStrRadixErrKind {
/// A character in the input string is not valid for the given radix.
InvalidCharacter,

/// The input length is not valid for the given radix.
InvalidLength,

/// The given radix is not supported.
UnsupportedRadix,
}

#[derive(Debug)]
enum FromStrRadixErrSrc {
Hex(FromHexError),
Dec(FromDecStrErr),
}

impl fmt::Display for FromStrRadixErrSrc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FromStrRadixErrSrc::Dec(d) => write!(f, "{}", d),
FromStrRadixErrSrc::Hex(h) => write!(f, "{}", h),
}
}
}

/// The error type for parsing numbers from strings.
#[derive(Debug)]
pub struct FromStrRadixErr {
kind: FromStrRadixErrKind,
source: Option<FromStrRadixErrSrc>,
}

impl FromStrRadixErr {
#[doc(hidden)]
pub fn unsupported() -> Self {
Self { kind: FromStrRadixErrKind::UnsupportedRadix, source: None }
}

/// Returns the corresponding `FromStrRadixErrKind` for this error.
pub fn kind(&self) -> FromStrRadixErrKind {
self.kind
}
}

impl fmt::Display for FromStrRadixErr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref src) = self.source {
return write!(f, "{}", src);
}

match self.kind {
FromStrRadixErrKind::UnsupportedRadix => write!(f, "the given radix is not supported"),
FromStrRadixErrKind::InvalidCharacter => write!(f, "input contains an invalid character"),
FromStrRadixErrKind::InvalidLength => write!(f, "length not supported for radix or type"),
}
}
}

#[cfg(feature = "std")]
impl std::error::Error for FromStrRadixErr {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self.source {
Some(FromStrRadixErrSrc::Dec(ref d)) => Some(d),
Some(FromStrRadixErrSrc::Hex(ref h)) => Some(h),
None => None,
}
}
}

impl From<FromDecStrErr> for FromStrRadixErr {
fn from(e: FromDecStrErr) -> Self {
let kind = match e {
FromDecStrErr::InvalidCharacter => FromStrRadixErrKind::InvalidCharacter,
FromDecStrErr::InvalidLength => FromStrRadixErrKind::InvalidLength,
};

Self { kind, source: Some(FromStrRadixErrSrc::Dec(e)) }
}
}

impl From<FromHexError> for FromStrRadixErr {
fn from(e: FromHexError) -> Self {
let kind = match e.inner {
hex::FromHexError::InvalidHexCharacter { .. } => FromStrRadixErrKind::InvalidCharacter,
hex::FromHexError::InvalidStringLength => FromStrRadixErrKind::InvalidLength,
hex::FromHexError::OddLength => FromStrRadixErrKind::InvalidLength,
};

Self { kind, source: Some(FromStrRadixErrSrc::Hex(e)) }
}
}

/// Conversion from decimal string error
#[derive(Debug, PartialEq)]
pub enum FromDecStrErr {
Expand Down Expand Up @@ -493,6 +589,18 @@ macro_rules! construct_uint {
/// Maximum value.
pub const MAX: $name = $name([u64::max_value(); $n_words]);

/// Converts a string slice in a given base to an integer. Only supports radixes of 10
/// and 16.
pub fn from_str_radix(txt: &str, radix: u32) -> Result<Self, $crate::FromStrRadixErr> {
let parsed = match radix {
10 => Self::from_dec_str(txt)?,
16 => core::str::FromStr::from_str(txt)?,
_ => return Err($crate::FromStrRadixErr::unsupported()),
};

Ok(parsed)
}

/// Convert from a decimal string.
pub fn from_dec_str(value: &str) -> $crate::core_::result::Result<Self, $crate::FromDecStrErr> {
if !value.bytes().all(|b| b >= 48 && b <= 57) {
Expand Down