From 9609d2e72dca34a6810d7d1018a531b978acd479 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 10 Aug 2018 14:09:09 +0200 Subject: [PATCH 01/16] Remove asm and port over performance improvements from `bigint` --- uint/Cargo.toml | 7 +- uint/build.rs | 19 --- uint/src/uint.rs | 330 ++++----------------------------------- uint/tests/uint_tests.rs | 1 - 4 files changed, 35 insertions(+), 322 deletions(-) delete mode 100644 uint/build.rs diff --git a/uint/Cargo.toml b/uint/Cargo.toml index 8623c94..38c0f9f 100644 --- a/uint/Cargo.toml +++ b/uint/Cargo.toml @@ -4,12 +4,8 @@ homepage = "http://parity.io" repository = "https://github.com/paritytech/primitives" license = "MIT/Apache-2.0" name = "uint" -version = "0.2.2" +version = "0.3.0" authors = ["Parity Technologies "] -build = "build.rs" - -[build-dependencies] -rustc_version = "0.2" [dependencies] byteorder = { version = "1", default-features = false } @@ -24,7 +20,6 @@ quickcheck = "0.6" [features] std = ["rustc-hex", "byteorder/std"] heapsizeof = ["heapsize"] -use_asm = [] impl_quickcheck_arbitrary = ["quickcheck"] [[example]] diff --git a/uint/build.rs b/uint/build.rs deleted file mode 100644 index 3ba1687..0000000 --- a/uint/build.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2015-2017 Parity Technologies -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -extern crate rustc_version; - -use rustc_version::{version_meta, Channel}; - -fn main() { - if cfg!(feature = "use_asm") { - if let Channel::Nightly = version_meta().unwrap().channel { - println!("cargo:rustc-cfg=asm_available"); - } - } -} diff --git a/uint/src/uint.rs b/uint/src/uint.rs index 9a72a99..0078856 100644 --- a/uint/src/uint.rs +++ b/uint/src/uint.rs @@ -50,7 +50,6 @@ macro_rules! impl_map_from { } } -#[cfg(not(all(asm_available, target_arch="x86_64")))] #[macro_export] #[doc(hidden)] macro_rules! uint_overflowing_add { @@ -73,85 +72,6 @@ macro_rules! uint_overflowing_add_reg { }) } -#[cfg(all(asm_available, target_arch="x86_64"))] -#[macro_export] -#[doc(hidden)] -macro_rules! uint_overflowing_add { - (U256, $n_words: tt, $self_expr: expr, $other: expr) => ({ - let mut result: [u64; $n_words] = unsafe { ::core::mem::uninitialized() }; - let self_t: &[u64; $n_words] = &$self_expr.0; - let other_t: &[u64; $n_words] = &$other.0; - - let overflow: u8; - unsafe { - asm!(" - add $9, $0 - adc $10, $1 - adc $11, $2 - adc $12, $3 - setc %al - " - : "=r"(result[0]), "=r"(result[1]), "=r"(result[2]), "=r"(result[3]), "={al}"(overflow) - : "0"(self_t[0]), "1"(self_t[1]), "2"(self_t[2]), "3"(self_t[3]), - "mr"(other_t[0]), "mr"(other_t[1]), "mr"(other_t[2]), "mr"(other_t[3]) - : - : - ); - } - (U256(result), overflow != 0) - }); - (U512, $n_words: tt, $self_expr: expr, $other: expr) => ({ - let mut result: [u64; $n_words] = unsafe { ::core::mem::uninitialized() }; - let self_t: &[u64; $n_words] = &$self_expr.0; - let other_t: &[u64; $n_words] = &$other.0; - - let overflow: u8; - - unsafe { - asm!(" - add $15, $0 - adc $16, $1 - adc $17, $2 - adc $18, $3 - lodsq - adc $11, %rax - stosq - lodsq - adc $12, %rax - stosq - lodsq - adc $13, %rax - stosq - lodsq - adc $14, %rax - stosq - setc %al - - ": "=r"(result[0]), "=r"(result[1]), "=r"(result[2]), "=r"(result[3]), - - "={al}"(overflow) /* $0 - $4 */ - - : "{rdi}"(&result[4] as *const u64) /* $5 */ - "{rsi}"(&other_t[4] as *const u64) /* $6 */ - "0"(self_t[0]), "1"(self_t[1]), "2"(self_t[2]), "3"(self_t[3]), - "m"(self_t[4]), "m"(self_t[5]), "m"(self_t[6]), "m"(self_t[7]), - /* $7 - $14 */ - - "mr"(other_t[0]), "mr"(other_t[1]), "mr"(other_t[2]), "mr"(other_t[3]), - "m"(other_t[4]), "m"(other_t[5]), "m"(other_t[6]), "m"(other_t[7]) /* $15 - $22 */ - : "rdi", "rsi" - : - ); - } - (U512(result), overflow != 0) - }); - - ($name:ident, $n_words: tt, $self_expr: expr, $other: expr) => ( - uint_overflowing_add_reg!($name, $n_words, $self_expr, $other) - ) -} - -#[cfg(not(all(asm_available, target_arch="x86_64")))] #[macro_export] #[doc(hidden)] macro_rules! uint_overflowing_sub { @@ -219,204 +139,6 @@ macro_rules! uint_overflowing_sub_reg { }) } -#[cfg(all(asm_available, target_arch="x86_64"))] -#[macro_export] -#[doc(hidden)] -macro_rules! uint_overflowing_sub { - (U256, $n_words: tt, $self_expr: expr, $other: expr) => ({ - let mut result: [u64; $n_words] = unsafe { ::core::mem::uninitialized() }; - let self_t: &[u64; $n_words] = &$self_expr.0; - let other_t: &[u64; $n_words] = &$other.0; - - let overflow: u8; - unsafe { - asm!(" - sub $9, $0 - sbb $10, $1 - sbb $11, $2 - sbb $12, $3 - setb %al - " - : "=r"(result[0]), "=r"(result[1]), "=r"(result[2]), "=r"(result[3]), "={al}"(overflow) - : "0"(self_t[0]), "1"(self_t[1]), "2"(self_t[2]), "3"(self_t[3]), "mr"(other_t[0]), "mr"(other_t[1]), "mr"(other_t[2]), "mr"(other_t[3]) - : - : - ); - } - (U256(result), overflow != 0) - }); - (U512, $n_words: tt, $self_expr: expr, $other: expr) => ({ - let mut result: [u64; $n_words] = unsafe { ::core::mem::uninitialized() }; - let self_t: &[u64; $n_words] = &$self_expr.0; - let other_t: &[u64; $n_words] = &$other.0; - - let overflow: u8; - - unsafe { - asm!(" - sub $15, $0 - sbb $16, $1 - sbb $17, $2 - sbb $18, $3 - lodsq - sbb $19, %rax - stosq - lodsq - sbb $20, %rax - stosq - lodsq - sbb $21, %rax - stosq - lodsq - sbb $22, %rax - stosq - setb %al - " - : "=r"(result[0]), "=r"(result[1]), "=r"(result[2]), "=r"(result[3]), - - "={al}"(overflow) /* $0 - $4 */ - - : "{rdi}"(&result[4] as *const u64) /* $5 */ - "{rsi}"(&self_t[4] as *const u64) /* $6 */ - "0"(self_t[0]), "1"(self_t[1]), "2"(self_t[2]), "3"(self_t[3]), - "m"(self_t[4]), "m"(self_t[5]), "m"(self_t[6]), "m"(self_t[7]), - /* $7 - $14 */ - - "m"(other_t[0]), "m"(other_t[1]), "m"(other_t[2]), "m"(other_t[3]), - "m"(other_t[4]), "m"(other_t[5]), "m"(other_t[6]), "m"(other_t[7]) /* $15 - $22 */ - : "rdi", "rsi" - : - ); - } - (U512(result), overflow != 0) - }); - ($name:ident, $n_words: tt, $self_expr: expr, $other: expr) => ({ - uint_overflowing_sub_reg!($name, $n_words, $self_expr, $other) - }) -} - -#[cfg(all(asm_available, target_arch="x86_64"))] -#[macro_export] -macro_rules! uint_overflowing_mul { - (U256, $n_words: tt, $self_expr: expr, $other: expr) => ({ - let mut result: [u64; $n_words] = unsafe { ::core::mem::uninitialized() }; - let self_t: &[u64; $n_words] = &$self_expr.0; - let other_t: &[u64; $n_words] = &$other.0; - - let overflow: u64; - unsafe { - asm!(" - mov $5, %rax - mulq $9 - mov %rax, $0 - mov %rdx, $1 - - mov $5, %rax - mulq $10 - add %rax, $1 - adc $$0, %rdx - mov %rdx, $2 - - mov $5, %rax - mulq $11 - add %rax, $2 - adc $$0, %rdx - mov %rdx, $3 - - mov $5, %rax - mulq $12 - add %rax, $3 - adc $$0, %rdx - mov %rdx, %rcx - - mov $6, %rax - mulq $9 - add %rax, $1 - adc %rdx, $2 - adc $$0, $3 - adc $$0, %rcx - - mov $6, %rax - mulq $10 - add %rax, $2 - adc %rdx, $3 - adc $$0, %rcx - adc $$0, $3 - adc $$0, %rcx - - mov $6, %rax - mulq $11 - add %rax, $3 - adc $$0, %rdx - or %rdx, %rcx - - mov $7, %rax - mulq $9 - add %rax, $2 - adc %rdx, $3 - adc $$0, %rcx - - mov $7, %rax - mulq $10 - add %rax, $3 - adc $$0, %rdx - or %rdx, %rcx - - mov $8, %rax - mulq $9 - add %rax, $3 - or %rdx, %rcx - - cmpq $$0, %rcx - jne 2f - - mov $8, %rcx - jrcxz 12f - - mov $12, %rcx - mov $11, %rax - or %rax, %rcx - mov $10, %rax - or %rax, %rcx - jmp 2f - - 12: - mov $12, %rcx - jrcxz 11f - - mov $7, %rcx - mov $6, %rax - or %rax, %rcx - - cmpq $$0, %rcx - jne 2f - - 11: - mov $11, %rcx - jrcxz 2f - mov $7, %rcx - - 2: - " - : /* $0 */ "={r8}"(result[0]), /* $1 */ "={r9}"(result[1]), /* $2 */ "={r10}"(result[2]), - /* $3 */ "={r11}"(result[3]), /* $4 */ "={rcx}"(overflow) - - : /* $5 */ "m"(self_t[0]), /* $6 */ "m"(self_t[1]), /* $7 */ "m"(self_t[2]), - /* $8 */ "m"(self_t[3]), /* $9 */ "m"(other_t[0]), /* $10 */ "m"(other_t[1]), - /* $11 */ "m"(other_t[2]), /* $12 */ "m"(other_t[3]) - : "rax", "rdx" - : - - ); - } - (U256(result), overflow > 0) - }); - ($name:ident, $n_words: tt, $self_expr: expr, $other: expr) => ( - uint_overflowing_mul_reg!($name, $n_words, $self_expr, $other) - ) -} - -#[cfg(not(all(asm_available, target_arch="x86_64")))] #[macro_export] #[doc(hidden)] macro_rules! uint_overflowing_mul { @@ -428,37 +150,47 @@ macro_rules! uint_overflowing_mul { #[macro_export] #[doc(hidden)] macro_rules! uint_full_mul_reg { - ($name:ident, $n_words: tt, $self_expr:expr, $other:expr) => ({{ + ($name:ident, 8, $self_expr:expr, $other:expr) => { + uint_full_mul_reg!($name, 8, $self_expr, $other, |a, b| a != 0 || b != 0); + }; + ($name:ident, $n_words:tt, $self_expr:expr, $other:expr) => { + uint_full_mul_reg!($name, $n_words, $self_expr, $other, |_, _| true); + }; + ($name:ident, $n_words:tt, $self_expr:expr, $other:expr, $check:expr) => ({{ #![allow(unused_assignments)] let $name(ref me) = $self_expr; let $name(ref you) = $other; - let mut ret = [0u64; 2*$n_words]; + let mut ret = [0u64; $n_words * 2]; unroll! { for i in 0..$n_words { let mut carry = 0u64; - let (b_u, b_l) = $crate::split(you[i]); + let b = you[i]; unroll! { for j in 0..$n_words { - if me[j] != 0 || carry != 0 { - let a = $crate::split(me[j]); + if $check(me[j], carry) { + let a = me[j]; - // multiply parts - let (c_l, overflow_l) = $crate::mul_u32(a, b_l, ret[i + j]); - let (c_u, overflow_u) = $crate::mul_u32(a, b_u, c_l >> 32); - ret[i + j] = (c_l & 0xFFFFFFFF) + (c_u << 32); + let (hi, low) = $crate::split_u128(a as u128 * b as u128); - // No overflow here - let res = (c_u >> 32) + (overflow_u << 32); - // possible overflows - let (res, o1) = res.overflowing_add(overflow_l + carry); - let (res, o2) = res.overflowing_add(ret[i + j + 1]); - ret[i + j + 1] = res; + let overflow = { + let existing_low = &mut ret[i + j]; + let (low, o) = low.overflowing_add(*existing_low); + *existing_low = low; + o + }; - // Only single overflow possible there - carry = (o1 | o2) as u64; + carry = { + let existing_hi = &mut ret[i + j + 1]; + let hi = hi + overflow as u64; + let (hi, o0) = hi.overflowing_add(carry); + let (hi, o1) = hi.overflowing_add(*existing_hi); + *existing_hi = hi; + + (o0 | o1) as u64 + } } } } @@ -466,7 +198,7 @@ macro_rules! uint_full_mul_reg { } ret - }}) + }}); } #[macro_export] @@ -606,6 +338,12 @@ pub fn split(a: u64) -> (u64, u64) { (a >> 32, a & 0xFFFF_FFFF) } +#[inline(always)] +#[doc(hidden)] +pub fn split_u128(a: u128) -> (u64, u64) { + ((a >> 64) as _, (a & 0xFFFFFFFFFFFFFFFF) as _) +} + #[macro_export] macro_rules! construct_uint { ($name:ident, $n_words: tt) => ( diff --git a/uint/tests/uint_tests.rs b/uint/tests/uint_tests.rs index 718cb8d..fd298bc 100644 --- a/uint/tests/uint_tests.rs +++ b/uint/tests/uint_tests.rs @@ -11,7 +11,6 @@ use std::u64::MAX; use std::str::FromStr; use uint::FromDecStrErr; -#[cfg(feature="std")] construct_uint!(U128, 2); construct_uint!(U256, 4); construct_uint!(U512, 8); From 16012b39435130c83fd405d6e27fd3c453f18ef6 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 10 Aug 2018 14:11:53 +0200 Subject: [PATCH 02/16] Add benchmark --- uint/benches/bigint.rs | 230 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 uint/benches/bigint.rs diff --git a/uint/benches/bigint.rs b/uint/benches/bigint.rs new file mode 100644 index 0000000..be8490b --- /dev/null +++ b/uint/benches/bigint.rs @@ -0,0 +1,230 @@ +// Copyright 2015-2017 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! benchmarking for bigint +//! should be started with: +//! ```bash +//! rustup run nightly cargo bench +//! ``` + +#![feature(test)] + +extern crate core; +extern crate test; +#[macro_use] +extern crate crunchy; +#[macro_use] +extern crate uint; +construct_uint!(U128, 2); +construct_uint!(U256, 4); +construct_uint!(U512, 8); + +use test::{Bencher, black_box}; + +impl U256 { + /// Multiplies two 256-bit integers to produce full 512-bit integer + /// No overflow possible + #[inline(always)] + pub fn full_mul(self, other: U256) -> U512 { + U512(uint_full_mul_reg!(U256, 4, self, other)) + } +} + +#[bench] +fn u256_add(b: &mut Bencher) { + b.iter(|| { + let n = black_box(10000); + let zero = black_box(U256::zero()); + (0..n).fold(zero, |old, new| { + old.overflowing_add(U256::from(black_box(new))).0 + }) + }); +} + +#[bench] +fn u256_sub(b: &mut Bencher) { + b.iter(|| { + let n = black_box(10000); + let max = black_box(U256::max_value()); + (0..n).fold(max, |old, new| { + old.overflowing_sub(U256::from(black_box(new))).0 + }) + }); +} + +#[bench] +fn u512_sub(b: &mut Bencher) { + b.iter(|| { + let n = black_box(10000); + let max = black_box(U512::max_value()); + (0..n).fold(max, |old, new| { + let new = black_box(new); + let p = new % 2; + old.overflowing_sub(U512([p, p, p, p, p, p, p, new])).0 + }) + }); +} + +#[bench] +fn u512_add(b: &mut Bencher) { + b.iter(|| { + let n = black_box(10000); + let zero = black_box(U512::zero()); + (0..n).fold(zero, |old, new| { + let new = black_box(new); + old.overflowing_add(U512([new, new, new, new, new, new, new, new])) + .0 + }) + }); +} + +#[bench] +fn u512_mul(b: &mut Bencher) { + b.iter(|| { + (1..10000).fold(black_box(U512::one()), |old, new| { + old.overflowing_mul(U512::from(black_box(new | 1))).0 + }) + }); +} + +#[bench] +fn u512_mul_small(b: &mut Bencher) { + b.iter(|| { + (1..153).fold(black_box(U512::one()), |old, _| { + old.overflowing_mul(U512::from(black_box(10))).0 + }) + }); +} + +#[bench] +fn u256_mul(b: &mut Bencher) { + b.iter(|| { + (1..10000).fold(black_box(U256::one()), |old, new| { + old.overflowing_mul(U256::from(black_box(new | 1))).0 + }) + }); +} + +#[bench] +fn u256_mul_small(b: &mut Bencher) { + b.iter(|| { + (1..77).fold(black_box(U256::one()), |old, _| { + old.overflowing_mul(U256::from(black_box(10))).0 + }) + }); +} + +#[bench] +fn u256_full_mul(b: &mut Bencher) { + b.iter(|| { + let n = black_box(10000); + let one = black_box(U256::one()); + (1..n).map(|n| n | 1).fold(one, |old, new| { + let new = black_box(new); + let U512(ref u512words) = old.full_mul(U256([new, new, new, new])); + U256([u512words[0], u512words[2], u512words[2], u512words[3]]) + }) + }); +} + + +#[bench] +fn u128_mul(b: &mut Bencher) { + b.iter(|| { + let n = black_box(10000); + (1..n).fold(U128([12345u64, 0u64]), |old, new| { + old.overflowing_mul(U128::from(new | 1)).0 + }) + }); +} + +#[bench] +fn u256_from_le(b: &mut Bencher) { + b.iter(|| { + let raw = black_box( + [ + 1u8, + 2, + 3, + 5, + 7, + 11, + 13, + 17, + 19, + 23, + 29, + 31, + 37, + 41, + 43, + 47, + 53, + 59, + 61, + 67, + 71, + 73, + 79, + 83, + 89, + 97, + 101, + 103, + 107, + 109, + 113, + 127, + ], + ); + let _ = U256::from_little_endian(&raw[..]); + }); +} + +#[bench] +fn u256_from_be(b: &mut Bencher) { + b.iter(|| { + let raw = black_box( + [ + 1u8, + 2, + 3, + 5, + 7, + 11, + 13, + 17, + 19, + 23, + 29, + 31, + 37, + 41, + 43, + 47, + 53, + 59, + 61, + 67, + 71, + 73, + 79, + 83, + 89, + 97, + 101, + 103, + 107, + 109, + 113, + 127, + ], + ); + let _ = U256::from_big_endian(&raw[..]); + }); +} From ae247ae84dfd8129e17c100da228504d2467dd27 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 10 Aug 2018 17:53:55 +0200 Subject: [PATCH 03/16] Port over no_std formatting and use rustc_hex 2.0 to make tests pass in no_std --- uint/Cargo.toml | 5 +-- uint/src/lib.rs | 9 ++++-- uint/src/uint.rs | 70 ++++++++++++++++++---------------------- uint/tests/uint_tests.rs | 28 +++++++++++++++- 4 files changed, 68 insertions(+), 44 deletions(-) diff --git a/uint/Cargo.toml b/uint/Cargo.toml index 38c0f9f..4d4bdd0 100644 --- a/uint/Cargo.toml +++ b/uint/Cargo.toml @@ -10,7 +10,8 @@ authors = ["Parity Technologies "] [dependencies] byteorder = { version = "1", default-features = false } heapsize = { version = "0.4.2", optional = true } -rustc-hex = { version = "1.0", optional = true } +# rustc-hex = { version = "2.0", optional = true } +rustc-hex = "2.0" quickcheck = { version = "0.6", optional = true } [dev-dependencies] @@ -18,7 +19,7 @@ crunchy = "0.1.5" quickcheck = "0.6" [features] -std = ["rustc-hex", "byteorder/std"] +std = ["byteorder/std"] heapsizeof = ["heapsize"] impl_quickcheck_arbitrary = ["quickcheck"] diff --git a/uint/src/lib.rs b/uint/src/lib.rs index 40a3a8d..1cccac5 100644 --- a/uint/src/lib.rs +++ b/uint/src/lib.rs @@ -8,8 +8,8 @@ //! Efficient large, fixed-size big integers and hashes. -#![cfg_attr(asm_available, feature(asm))] -#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature="std"), no_std)] +#![cfg_attr(all(not(feature="std"), test), feature(alloc))] #[doc(hidden)] pub extern crate byteorder; @@ -22,7 +22,6 @@ pub extern crate heapsize; #[doc(hidden)] pub extern crate core; -#[cfg(feature = "std")] #[doc(hidden)] pub extern crate rustc_hex; @@ -30,5 +29,9 @@ pub extern crate rustc_hex; #[doc(hidden)] pub extern crate quickcheck; +#[cfg(all(not(feature = "std"), test))] +#[macro_use] +extern crate alloc; + mod uint; pub use uint::*; diff --git a/uint/src/uint.rs b/uint/src/uint.rs index 0078856..ebcbab5 100644 --- a/uint/src/uint.rs +++ b/uint/src/uint.rs @@ -350,6 +350,7 @@ macro_rules! construct_uint { /// Little-endian large integer type #[repr(C)] #[derive(Copy, Clone, Eq, PartialEq, Hash)] + // TODO: serialize stuff? #[cfg_attr(feature="serialize", derive(Serialize, Deserialize))] pub struct $name(pub [u64; $n_words]); impl AsRef<$name> for $name { @@ -365,6 +366,7 @@ macro_rules! construct_uint { } impl $name { + pub const MAX: $name = $name([u64::max_value(); $n_words]); /// Convert from a decimal string. pub fn from_dec_str(value: &str) -> Result { if !value.bytes().all(|b| b >= 48 && b <= 57) { @@ -1181,35 +1183,9 @@ macro_rules! construct_uint { } } - impl_std_for_uint!($name, $n_words); - impl_heapsize_for_uint!($name); - // `$n_words * 8` because macro expects bytes and - // uints use 64 bit (8 byte) words - impl_quickcheck_arbitrary_for_uint!($name, ($n_words * 8)); - ); -} - -#[cfg(feature="std")] -#[macro_export] -#[doc(hidden)] -macro_rules! impl_std_for_uint_internals { - ($name: ident, $n_words: tt) => { - /// Convert to hex string. - #[deprecated(note = "Use LowerHex instead.")] - pub fn to_hex(&self) -> String { - format!("{:x}", self) - } - } -} - -#[cfg(feature="std")] -#[macro_export] -#[doc(hidden)] -macro_rules! impl_std_for_uint { - ($name: ident, $n_words: tt) => { impl ::core::fmt::Debug for $name { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{:#x}", self) + ::core::fmt::Display::fmt(self, f) } } @@ -1219,16 +1195,24 @@ macro_rules! impl_std_for_uint { return write!(f, "0"); } - let mut s = String::new(); + let mut buf = [0_u8; $n_words*20]; + let mut i = buf.len() - 1; let mut current = *self; let ten = $name::from(10); - while !current.is_zero() { - s = format!("{}{}", (current % ten).low_u32(), s); + loop { + let digit = (current % ten).low_u64() as u8; + buf[i] = digit + b'0'; current = current / ten; + if current.is_zero() { + break; + } + i -= 1; } - write!(f, "{}", s) + // sequence of `'0'..'9'` chars is guaranteed to be a valid UTF8 string + let s = unsafe {::core::str::from_utf8_unchecked(&buf[i..])}; + f.write_str(s) } } @@ -1237,10 +1221,9 @@ macro_rules! impl_std_for_uint { fn from_str(value: &str) -> Result<$name, Self::Err> { use $crate::rustc_hex::FromHex; - let bytes: Vec = match value.len() % 2 == 0 { - true => try!(value.from_hex()), - false => try!(("0".to_owned() + value).from_hex()) + true => value.from_hex()?, + false => ("0".to_owned() + value).from_hex()? }; let bytes_ref: &[u8] = &bytes; @@ -1281,14 +1264,25 @@ macro_rules! impl_std_for_uint { s.parse().unwrap() } } - } + + impl_heapsize_for_uint!($name); + // `$n_words * 8` because macro expects bytes and + // uints use 64 bit (8 byte) words + impl_quickcheck_arbitrary_for_uint!($name, ($n_words * 8)); + ); } -#[cfg(not(feature="std"))] +#[cfg(feature="std")] #[macro_export] #[doc(hidden)] -macro_rules! impl_std_for_uint { - ($name: ident, $n_words: tt) => {} +macro_rules! impl_std_for_uint_internals { + ($name: ident, $n_words: tt) => { + /// Convert to hex string. + #[deprecated(note = "Use LowerHex instead.")] + pub fn to_hex(&self) -> String { + format!("{:x}", self) + } + } } #[cfg(not(feature="std"))] diff --git a/uint/tests/uint_tests.rs b/uint/tests/uint_tests.rs index fd298bc..5272e2c 100644 --- a/uint/tests/uint_tests.rs +++ b/uint/tests/uint_tests.rs @@ -4,6 +4,7 @@ extern crate core; extern crate uint; #[macro_use] extern crate crunchy; +#[cfg(feature = "impl_quickcheck_arbitrary")] #[macro_use] extern crate quickcheck; @@ -346,7 +347,8 @@ fn uint256_pow_overflow_panic() { fn should_format_and_debug_correctly() { let test = |x: usize, hex: &'static str, display: &'static str| { assert_eq!(format!("{}", U256::from(x)), display); - assert_eq!(format!("{:?}", U256::from(x)), format!("0x{}", hex)); + // TODO: proper impl for Debug so we get this to pass: assert_eq!(format!("{:?}", U256::from(x)), format!("0x{}", hex)); + assert_eq!(format!("{:?}", U256::from(x)), display); assert_eq!(format!("{:x}", U256::from(x)), hex); assert_eq!(format!("{:#x}", U256::from(x)), format!("0x{}", hex)); }; @@ -360,6 +362,30 @@ fn should_format_and_debug_correctly() { test(0x1000, "1000", "4096"); } +#[test] +pub fn display_u128() { + let expected = "340282366920938463463374607431768211455"; + let value = U128::MAX; + assert_eq!(format!("{}", value), expected); + assert_eq!(format!("{:?}", value), expected); +} + +#[test] +pub fn display_u256() { + let expected = "115792089237316195423570985008687907853269984665640564039457584007913129639935"; + let value = U256::MAX; + assert_eq!(format!("{}", value), expected); + assert_eq!(format!("{:?}", value), expected); +} + +#[test] +pub fn display_u512() { + let expected = "13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095"; + let value = U512::MAX; + assert_eq!(format!("{}", value), expected); + assert_eq!(format!("{:?}", value), expected); +} + #[test] fn uint256_overflowing_pow() { assert_eq!( From 13d73566571c4a4bdad8d15f2cf863e06ea8fd93 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 10 Aug 2018 17:56:05 +0200 Subject: [PATCH 04/16] Run uint no_std tests --- .travis.yml | 1 + uint/Cargo.toml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2b14aa1..a3ca30f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,5 +9,6 @@ script: - cargo build - cargo test --all --exclude uint - cd uint/ && cargo test --features=std,impl_quickcheck_arbitrary && cd .. + - cd uint/ && cargo test --no-default-features && cd .. - cd hashdb/ && cargo test --no-default-features && cd .. - cd plain_hasher/ && cargo test --no-default-features && cd .. diff --git a/uint/Cargo.toml b/uint/Cargo.toml index 4d4bdd0..cedc21e 100644 --- a/uint/Cargo.toml +++ b/uint/Cargo.toml @@ -10,7 +10,6 @@ authors = ["Parity Technologies "] [dependencies] byteorder = { version = "1", default-features = false } heapsize = { version = "0.4.2", optional = true } -# rustc-hex = { version = "2.0", optional = true } rustc-hex = "2.0" quickcheck = { version = "0.6", optional = true } From 33567263fa04856d33e630a78da40a5ef9498232 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 10 Aug 2018 19:38:14 +0200 Subject: [PATCH 05/16] Use rustc-hex without default features --- uint/Cargo.toml | 5 +++-- uint/src/lib.rs | 1 - uint/tests/uint_tests.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/uint/Cargo.toml b/uint/Cargo.toml index cedc21e..b8c669f 100644 --- a/uint/Cargo.toml +++ b/uint/Cargo.toml @@ -10,15 +10,16 @@ authors = ["Parity Technologies "] [dependencies] byteorder = { version = "1", default-features = false } heapsize = { version = "0.4.2", optional = true } -rustc-hex = "2.0" +rustc-hex = { version = "2.0", default-features = false } quickcheck = { version = "0.6", optional = true } [dev-dependencies] crunchy = "0.1.5" quickcheck = "0.6" +rustc-hex = "2.0" [features] -std = ["byteorder/std"] +std = ["byteorder/std", "rustc-hex/std"] heapsizeof = ["heapsize"] impl_quickcheck_arbitrary = ["quickcheck"] diff --git a/uint/src/lib.rs b/uint/src/lib.rs index 1cccac5..b9b5b20 100644 --- a/uint/src/lib.rs +++ b/uint/src/lib.rs @@ -30,7 +30,6 @@ pub extern crate rustc_hex; pub extern crate quickcheck; #[cfg(all(not(feature = "std"), test))] -#[macro_use] extern crate alloc; mod uint; diff --git a/uint/tests/uint_tests.rs b/uint/tests/uint_tests.rs index 5272e2c..d6d2d2d 100644 --- a/uint/tests/uint_tests.rs +++ b/uint/tests/uint_tests.rs @@ -8,8 +8,8 @@ extern crate crunchy; #[macro_use] extern crate quickcheck; -use std::u64::MAX; -use std::str::FromStr; +use core::u64::MAX; +use core::str::FromStr; use uint::FromDecStrErr; construct_uint!(U128, 2); From 4336907eac770c82fb81458ac6082d322d199813 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 10 Aug 2018 19:45:11 +0200 Subject: [PATCH 06/16] move display/debug funtionality back to `std`-gated macro --- uint/src/uint.rs | 54 +++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/uint/src/uint.rs b/uint/src/uint.rs index ebcbab5..2d1c640 100644 --- a/uint/src/uint.rs +++ b/uint/src/uint.rs @@ -1183,6 +1183,39 @@ macro_rules! construct_uint { } } + impl_std_for_uint!($name, $n_words); + impl_heapsize_for_uint!($name); + // `$n_words * 8` because macro expects bytes and + // uints use 64 bit (8 byte) words + impl_quickcheck_arbitrary_for_uint!($name, ($n_words * 8)); + ); +} + +#[cfg(feature="std")] +#[macro_export] +#[doc(hidden)] +macro_rules! impl_std_for_uint_internals { + ($name: ident, $n_words: tt) => { + /// Convert to hex string. + #[deprecated(note = "Use LowerHex instead.")] + pub fn to_hex(&self) -> String { + format!("{:x}", self) + } + } +} + +#[cfg(not(feature="std"))] +#[macro_export] +#[doc(hidden)] +macro_rules! impl_std_for_uint_internals { + ($name: ident, $n_words: tt) => {} +} + +#[cfg(feature="std")] +#[macro_export] +#[doc(hidden)] +macro_rules! impl_std_for_uint { + ($name: ident, $n_words: tt) => { impl ::core::fmt::Debug for $name { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Display::fmt(self, f) @@ -1264,34 +1297,17 @@ macro_rules! construct_uint { s.parse().unwrap() } } - - impl_heapsize_for_uint!($name); - // `$n_words * 8` because macro expects bytes and - // uints use 64 bit (8 byte) words - impl_quickcheck_arbitrary_for_uint!($name, ($n_words * 8)); - ); -} - -#[cfg(feature="std")] -#[macro_export] -#[doc(hidden)] -macro_rules! impl_std_for_uint_internals { - ($name: ident, $n_words: tt) => { - /// Convert to hex string. - #[deprecated(note = "Use LowerHex instead.")] - pub fn to_hex(&self) -> String { - format!("{:x}", self) - } } } #[cfg(not(feature="std"))] #[macro_export] #[doc(hidden)] -macro_rules! impl_std_for_uint_internals { +macro_rules! impl_std_for_uint { ($name: ident, $n_words: tt) => {} } + #[cfg(feature="heapsizeof")] #[macro_export] #[doc(hidden)] From 2af9e11af78dbd519a52e0cd6fa47b623c8c7998 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 10 Aug 2018 19:47:02 +0200 Subject: [PATCH 07/16] don't run tests with --no-default-features --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a3ca30f..2b14aa1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,5 @@ script: - cargo build - cargo test --all --exclude uint - cd uint/ && cargo test --features=std,impl_quickcheck_arbitrary && cd .. - - cd uint/ && cargo test --no-default-features && cd .. - cd hashdb/ && cargo test --no-default-features && cd .. - cd plain_hasher/ && cargo test --no-default-features && cd .. From 1262de484f460368b8d36705d0f3ae11b36c6137 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 13 Aug 2018 10:49:53 +0200 Subject: [PATCH 08/16] Try `travis_wait` to fix timeouts --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2b14aa1..5865025 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,6 @@ matrix: script: - cargo build - cargo test --all --exclude uint - - cd uint/ && cargo test --features=std,impl_quickcheck_arbitrary && cd .. + - cd uint/ && travis_wait cargo test --features=std,impl_quickcheck_arbitrary && cd .. - cd hashdb/ && cargo test --no-default-features && cd .. - cd plain_hasher/ && cargo test --no-default-features && cd .. From 78cf6e7b0f12f68c0675c1d90686074c2334024a Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 13 Aug 2018 11:41:38 +0200 Subject: [PATCH 09/16] Debug travis --- .travis.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5865025..d35cdda 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,7 @@ language: rust rust: - - stable - nightly matrix: - allow_failures: - - rust: nightly + - rust: nightly script: - - cargo build - - cargo test --all --exclude uint - cd uint/ && travis_wait cargo test --features=std,impl_quickcheck_arbitrary && cd .. - - cd hashdb/ && cargo test --no-default-features && cd .. - - cd plain_hasher/ && cargo test --no-default-features && cd .. From b49e0ed13a32c9d61e392d7f2b0e0f4c7d5dde57 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 13 Aug 2018 12:14:39 +0200 Subject: [PATCH 10/16] debug travis (attempt 3) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d35cdda..03ab986 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,4 +4,4 @@ rust: matrix: - rust: nightly script: - - cd uint/ && travis_wait cargo test --features=std,impl_quickcheck_arbitrary && cd .. + - cd uint/ && travis_wait 40 cargo test --features=std,impl_quickcheck_arbitrary && cd .. From 85ba498a4bb8bb343a0dc24c4eb039c35d8ffef8 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 13 Aug 2018 13:06:42 +0200 Subject: [PATCH 11/16] Try with absurdly long wait time: 400min (attempt 4) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 03ab986..b813e1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,4 +4,4 @@ rust: matrix: - rust: nightly script: - - cd uint/ && travis_wait 40 cargo test --features=std,impl_quickcheck_arbitrary && cd .. + - cd uint/ && travis_wait 400 cargo test --features=std,impl_quickcheck_arbitrary && cd .. From 97ecf175c8b62ea82a4ffe1c382abac8be1b0cc2 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 13 Aug 2018 13:56:23 +0200 Subject: [PATCH 12/16] Export U256 and U512 --- uint/Cargo.toml | 2 +- uint/README.md | 6 +++--- uint/src/lib.rs | 3 +++ uint/src/uint.rs | 3 +++ uint/tests/uint_tests.rs | 4 +--- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/uint/Cargo.toml b/uint/Cargo.toml index b8c669f..8120e7c 100644 --- a/uint/Cargo.toml +++ b/uint/Cargo.toml @@ -12,9 +12,9 @@ byteorder = { version = "1", default-features = false } heapsize = { version = "0.4.2", optional = true } rustc-hex = { version = "2.0", default-features = false } quickcheck = { version = "0.6", optional = true } +crunchy = "0.1" [dev-dependencies] -crunchy = "0.1.5" quickcheck = "0.6" rustc-hex = "2.0" diff --git a/uint/README.md b/uint/README.md index 4a0388b..9ba5885 100644 --- a/uint/README.md +++ b/uint/README.md @@ -1,8 +1,8 @@ # Big unsigned integer types Implementation of a various large-but-fixed sized unsigned integer types. -The functions here are designed to be fast. There are optional `x86_64` -implementations for even more speed, hidden behind the `x64_arithmetic` -feature flag. +The functions here are designed to be fast. + +The crate exports two commonly used types: `U256` and `U512`. Other sizes can be constructed with `construct_uint!(NAME, SIZE_IN_WORDS)`, e.g. `construct_uint!(U128, 2);`. Run tests with `cargo test --features=std,impl_quickcheck_arbitrary`. \ No newline at end of file diff --git a/uint/src/lib.rs b/uint/src/lib.rs index b9b5b20..e53f3c8 100644 --- a/uint/src/lib.rs +++ b/uint/src/lib.rs @@ -32,5 +32,8 @@ pub extern crate quickcheck; #[cfg(all(not(feature = "std"), test))] extern crate alloc; +#[macro_use] +extern crate crunchy; + mod uint; pub use uint::*; diff --git a/uint/src/uint.rs b/uint/src/uint.rs index 2d1c640..a25c934 100644 --- a/uint/src/uint.rs +++ b/uint/src/uint.rs @@ -1367,3 +1367,6 @@ macro_rules! impl_quickcheck_arbitrary_for_uint { macro_rules! impl_quickcheck_arbitrary_for_uint { ($uint: ty, $n_bytes: tt) => {} } + +construct_uint!(U256, 4); +construct_uint!(U512, 8); \ No newline at end of file diff --git a/uint/tests/uint_tests.rs b/uint/tests/uint_tests.rs index d6d2d2d..ed233d1 100644 --- a/uint/tests/uint_tests.rs +++ b/uint/tests/uint_tests.rs @@ -10,11 +10,9 @@ extern crate quickcheck; use core::u64::MAX; use core::str::FromStr; -use uint::FromDecStrErr; +use uint::{U256, U512, FromDecStrErr}; construct_uint!(U128, 2); -construct_uint!(U256, 4); -construct_uint!(U512, 8); #[test] fn uint256_checked_ops() { From d3c335e7f7988606fa064b24813462906f00e94a Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 13 Aug 2018 15:54:21 +0200 Subject: [PATCH 13/16] Add impl to converto from U256 to [u8; 32] --- uint/src/uint.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/uint/src/uint.rs b/uint/src/uint.rs index a25c934..b684493 100644 --- a/uint/src/uint.rs +++ b/uint/src/uint.rs @@ -1369,4 +1369,12 @@ macro_rules! impl_quickcheck_arbitrary_for_uint { } construct_uint!(U256, 4); -construct_uint!(U512, 8); \ No newline at end of file +construct_uint!(U512, 8); + +impl From for [u8; 32] { + fn from(number: U256) -> Self { + let mut arr = [0u8; 32]; + number.to_big_endian(&mut arr); + arr + } +} \ No newline at end of file From 19062594bf3f72107731ae5d2f8d16964d70a567 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 13 Aug 2018 15:55:39 +0200 Subject: [PATCH 14/16] Put original travis config back --- .travis.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b813e1f..2b14aa1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,13 @@ language: rust rust: + - stable - nightly matrix: - - rust: nightly + allow_failures: + - rust: nightly script: - - cd uint/ && travis_wait 400 cargo test --features=std,impl_quickcheck_arbitrary && cd .. + - cargo build + - cargo test --all --exclude uint + - cd uint/ && cargo test --features=std,impl_quickcheck_arbitrary && cd .. + - cd hashdb/ && cargo test --no-default-features && cd .. + - cd plain_hasher/ && cargo test --no-default-features && cd .. From b4b5c6f6268424dd0982b3b8d2c32444440a495e Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 13 Aug 2018 16:07:54 +0200 Subject: [PATCH 15/16] Fix repo meta-data key Use --release to run tests --- .travis.yml | 2 +- uint/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2b14aa1..c54a3b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,6 @@ matrix: script: - cargo build - cargo test --all --exclude uint - - cd uint/ && cargo test --features=std,impl_quickcheck_arbitrary && cd .. + - cd uint/ && cargo test --features=std,impl_quickcheck_arbitrary --release && cd .. - cd hashdb/ && cargo test --no-default-features && cd .. - cd plain_hasher/ && cargo test --no-default-features && cd .. diff --git a/uint/Cargo.toml b/uint/Cargo.toml index 8120e7c..190841e 100644 --- a/uint/Cargo.toml +++ b/uint/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Large fixed-size integers arithmetics" homepage = "http://parity.io" -repository = "https://github.com/paritytech/primitives" +repository = "https://github.com/paritytech/parity-common" license = "MIT/Apache-2.0" name = "uint" version = "0.3.0" From 9261b8519eef854da44b01b93970c91721b29f5e Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 13 Aug 2018 16:51:32 +0200 Subject: [PATCH 16/16] Move conversion inside macro --- uint/src/uint.rs | 17 ++++++++--------- uint/tests/uint_tests.rs | 12 ++++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/uint/src/uint.rs b/uint/src/uint.rs index b684493..dc2db06 100644 --- a/uint/src/uint.rs +++ b/uint/src/uint.rs @@ -806,6 +806,13 @@ macro_rules! construct_uint { } } + impl From<$name> for [u8; $n_words * 8] { + fn from(number: $name) -> Self { + let mut arr = [0u8; $n_words * 8]; + number.to_big_endian(&mut arr); + arr + } + } impl Default for $name { fn default() -> Self { $name::zero() @@ -1369,12 +1376,4 @@ macro_rules! impl_quickcheck_arbitrary_for_uint { } construct_uint!(U256, 4); -construct_uint!(U512, 8); - -impl From for [u8; 32] { - fn from(number: U256) -> Self { - let mut arr = [0u8; 32]; - number.to_big_endian(&mut arr); - arr - } -} \ No newline at end of file +construct_uint!(U512, 8); \ No newline at end of file diff --git a/uint/tests/uint_tests.rs b/uint/tests/uint_tests.rs index ed233d1..fdf1b71 100644 --- a/uint/tests/uint_tests.rs +++ b/uint/tests/uint_tests.rs @@ -1000,6 +1000,18 @@ fn from_big_endian() { assert_eq!(U256::from(1), number); } +#[test] +fn from_fixed_array() { + let expected: [u8; 32] = [ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, + ]; + let ary : [u8; 32] = U256::from(1).into(); + assert_eq!(ary, expected); +} + #[test] fn leading_zeros() { assert_eq!(U256::from("000000000000000000000001adbdd6bd6ff027485484b97f8a6a4c7129756dd1").leading_zeros(), 95);