From 8d5dd05454b057aa7e3d451660b12a9f6fea3866 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 1 Aug 2019 13:45:39 -0700 Subject: [PATCH] Use the alloc crate for no_std (Rust 1.36+) --- .travis.yml | 9 +++++++ ci/rustup.sh | 2 +- ci/test_full.sh | 43 +++++++++++++++++++++--------- src/algorithms.rs | 10 +++---- src/bigint.rs | 26 ++++++++++-------- src/bigrand.rs | 2 +- src/biguint.rs | 67 +++++++++++++++++++++++++++++++++-------------- src/lib.rs | 32 +++++++++++++++++++--- src/monty.rs | 5 ++-- tests/bigint.rs | 22 ++++++++-------- tests/biguint.rs | 24 ++++++++--------- 11 files changed, 162 insertions(+), 80 deletions(-) diff --git a/.travis.yml b/.travis.yml index 79474e17..606655d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ rust: - 1.22.0 # rand - 1.26.0 # has_i128 - 1.31.0 # 2018! + - 1.36.0 # alloc - stable - beta - nightly @@ -19,6 +20,14 @@ matrix: apt: packages: - gcc-multilib + # try a target that doesn't have std at all, but does have alloc + - name: "no_std" + rust: stable + env: TARGET=thumbv6m-none-eabi + before_script: + - rustup target add $TARGET + script: + - cargo build --verbose --target $TARGET --no-default-features --features i128 - name: "rustfmt" rust: 1.31.0 before_script: diff --git a/ci/rustup.sh b/ci/rustup.sh index c5aea794..5134459f 100755 --- a/ci/rustup.sh +++ b/ci/rustup.sh @@ -5,7 +5,7 @@ set -ex export TRAVIS_RUST_VERSION -for TRAVIS_RUST_VERSION in 1.15.0 1.22.0 1.26.0 stable beta nightly; do +for TRAVIS_RUST_VERSION in 1.15.0 1.22.0 1.26.0 1.31.0 1.36.0 stable beta nightly; do run="rustup run $TRAVIS_RUST_VERSION" $run cargo build --verbose $run $PWD/ci/test_full.sh diff --git a/ci/test_full.sh b/ci/test_full.sh index 4915e880..88bc4e42 100755 --- a/ci/test_full.sh +++ b/ci/test_full.sh @@ -4,16 +4,17 @@ set -ex echo Testing num-bigint on rustc ${TRAVIS_RUST_VERSION} -FEATURES="serde" -if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable.*|1.31.0|1.26.0|1.22.0)$ ]]; then - FEATURES="$FEATURES rand" -fi -if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable.*|1.31.0|1.26.0)$ ]]; then - FEATURES="$FEATURES i128" -fi -if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable.*|1.31.0)$ ]]; then - FEATURES="$FEATURES quickcheck quickcheck_macros" -fi +case "$TRAVIS_RUST_VERSION" in + 1.1[5-9].* | 1.2[0-1].*) STD_FEATURES="serde" ;; + 1.2[2-5].*) STD_FEATURES="serde rand" ;; + 1.2[6-9].* | 1.30.*) STD_FEATURES="serde rand i128" ;; + *) STD_FEATURES="serde rand i128 quickcheck quickcheck_macros" ;; +esac + +case "$TRAVIS_RUST_VERSION" in + 1.1[5-9].* | 1.2[0-9].* | 1.3[0-5].*) ;; + *) NO_STD_FEATURES="i128" ;; +esac # num-bigint should build and test everywhere. cargo build --verbose @@ -24,14 +25,30 @@ cargo build --no-default-features --features="std" cargo test --no-default-features --features="std" # Each isolated feature should also work everywhere. -for feature in $FEATURES; do +for feature in $STD_FEATURES; do cargo build --verbose --no-default-features --features="std $feature" cargo test --verbose --no-default-features --features="std $feature" done # test all supported features together -cargo build --features="std $FEATURES" -cargo test --features="std $FEATURES" +cargo build --features="std $STD_FEATURES" +cargo test --features="std $STD_FEATURES" + +if test -n "${NO_STD_FEATURES:+true}"; then + # It should build with minimal features too. + cargo build --no-default-features + cargo test --no-default-features + + # Each isolated feature should also work everywhere. + for feature in $NO_STD_FEATURES; do + cargo build --verbose --no-default-features --features="$feature" + cargo test --verbose --no-default-features --features="$feature" + done + + # test all supported features together + cargo build --no-default-features --features="$NO_STD_FEATURES" + cargo test --no-default-features --features="$NO_STD_FEATURES" +fi # make sure benchmarks can be built if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then diff --git a/src/algorithms.rs b/src/algorithms.rs index aa42c6c8..fc94584c 100644 --- a/src/algorithms.rs +++ b/src/algorithms.rs @@ -1,8 +1,8 @@ -use std::borrow::Cow; -use std::cmp; -use std::cmp::Ordering::{self, Equal, Greater, Less}; -use std::iter::repeat; -use std::mem; +use core::cmp; +use core::cmp::Ordering::{self, Equal, Greater, Less}; +use core::iter::repeat; +use core::mem; +use std_alloc::{Cow, Vec}; use traits; use traits::{One, Zero}; diff --git a/src/bigint.rs b/src/bigint.rs index d62767c2..defa2236 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -1,18 +1,22 @@ -#[allow(deprecated, unused_imports)] -use std::ascii::AsciiExt; -use std::cmp::Ordering::{self, Equal, Greater, Less}; -use std::default::Default; -use std::fmt; -use std::iter::{Product, Sum}; -use std::mem; -use std::ops::{ +use core::cmp::Ordering::{self, Equal, Greater, Less}; +use core::default::Default; +use core::fmt; +use core::iter::{Product, Sum}; +use core::mem; +use core::ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, }; -use std::str::{self, FromStr}; +use core::str::{self, FromStr}; #[cfg(has_i128)] -use std::{i128, u128}; -use std::{i64, u64}; +use core::{i128, u128}; +use core::{i64, u64}; +#[cfg(feature = "std")] +#[allow(deprecated, unused_imports)] +use std::ascii::AsciiExt; +use std_alloc::{String, Vec}; +#[cfg(feature = "quickcheck")] +use std_alloc::Box; #[cfg(feature = "serde")] use serde; diff --git a/src/bigrand.rs b/src/bigrand.rs index ab0e0ed8..6bc01787 100644 --- a/src/bigrand.rs +++ b/src/bigrand.rs @@ -58,7 +58,7 @@ impl RandBigInt for R { #[cfg(u64_digit)] fn gen_biguint(&mut self, bit_size: usize) -> BigUint { - use std::slice; + use core::slice; let (digits, rem) = bit_size.div_rem(&32); let native_digits = bit_size.div_ceil(&64); diff --git a/src/biguint.rs b/src/biguint.rs index ff30ac24..8cbd6613 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -1,27 +1,31 @@ -#[allow(deprecated, unused_imports)] -use std::ascii::AsciiExt; -use std::borrow::Cow; -use std::cmp; -use std::cmp::Ordering::{self, Equal, Greater, Less}; -use std::default::Default; -use std::fmt; -use std::iter::{Product, Sum}; -use std::mem; -use std::ops::{ +use core::cmp; +use core::cmp::Ordering::{self, Equal, Greater, Less}; +use core::default::Default; +use core::fmt; +use core::iter::{Product, Sum}; +use core::mem; +use core::ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, }; -use std::str::{self, FromStr}; -use std::{f32, f64}; -use std::{u32, u64, u8}; +use core::str::{self, FromStr}; +use core::{f32, f64}; +use core::{u32, u64, u8}; +#[cfg(feature = "std")] +#[allow(deprecated, unused_imports)] +use std::ascii::AsciiExt; +use std_alloc::{Cow, String, Vec}; +#[cfg(feature = "quickcheck")] +use std_alloc::Box; #[cfg(feature = "serde")] use serde; use integer::{Integer, Roots}; +use traits::float::FloatCore; use traits::{ - CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Float, FromPrimitive, Num, One, Pow, - ToPrimitive, Unsigned, Zero, + CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, FromPrimitive, Num, One, Pow, ToPrimitive, + Unsigned, Zero, }; use big_digit::{self, BigDigit}; @@ -215,10 +219,15 @@ fn from_radix_digits_be(v: &[u8], radix: u32) -> BigUint { debug_assert!(!v.is_empty() && !radix.is_power_of_two()); debug_assert!(v.iter().all(|&c| u32::from(c) < radix)); + #[cfg(feature = "std")] + let radix_log2 = f64::from(radix).log2(); + #[cfg(not(feature = "std"))] + let radix_log2 = ilog2(radix.next_power_of_two()) as f64; + // Estimate how big the result will be, so we can pre-allocate it. - let bits = f64::from(radix).log2() * v.len() as f64; + let bits = radix_log2 * v.len() as f64; let big_digits = (bits / big_digit::BITS as f64).ceil(); - let mut data = Vec::with_capacity(big_digits as usize); + let mut data = Vec::with_capacity(big_digits.to_usize().unwrap_or(0)); let (base, power) = get_radix_base(radix, big_digit::BITS); let radix = radix as BigDigit; @@ -1586,6 +1595,7 @@ impl Roots for BigUint { let max_bits = bits / n as usize + 1; + #[cfg(feature = "std")] let guess = if let Some(f) = self.to_f64() { // We fit in `f64` (lossy), so get a better initial guess from that. BigUint::from_f64((f.ln() / f64::from(n)).exp()).unwrap() @@ -1603,6 +1613,9 @@ impl Roots for BigUint { } }; + #[cfg(not(feature = "std"))] + let guess = BigUint::one() << max_bits; + let n_min_1 = n - 1; fixpoint(guess, max_bits, move |s| { let q = self / s.pow(n_min_1); @@ -1626,6 +1639,7 @@ impl Roots for BigUint { let bits = self.bits(); let max_bits = bits / 2 as usize + 1; + #[cfg(feature = "std")] let guess = if let Some(f) = self.to_f64() { // We fit in `f64` (lossy), so get a better initial guess from that. BigUint::from_f64(f.sqrt()).unwrap() @@ -1638,6 +1652,9 @@ impl Roots for BigUint { (self >> scale).sqrt() << root_scale }; + #[cfg(not(feature = "std"))] + let guess = BigUint::one() << max_bits; + fixpoint(guess, max_bits, move |s| { let q = self / s; let t = s + q; @@ -1658,6 +1675,7 @@ impl Roots for BigUint { let bits = self.bits(); let max_bits = bits / 3 as usize + 1; + #[cfg(feature = "std")] let guess = if let Some(f) = self.to_f64() { // We fit in `f64` (lossy), so get a better initial guess from that. BigUint::from_f64(f.cbrt()).unwrap() @@ -1670,6 +1688,9 @@ impl Roots for BigUint { (self >> scale).cbrt() << root_scale }; + #[cfg(not(feature = "std"))] + let guess = BigUint::one() << max_bits; + fixpoint(guess, max_bits, move |s| { let q = self / (s * s); let t = (s << 1) + q; @@ -1836,7 +1857,7 @@ impl FromPrimitive for BigUint { return Some(BigUint::zero()); } - let (mantissa, exponent, sign) = Float::integer_decode(n); + let (mantissa, exponent, sign) = FloatCore::integer_decode(n); if sign == -1 { return None; @@ -2011,9 +2032,15 @@ fn to_inexact_bitwise_digits_le(u: &BigUint, bits: usize) -> Vec { fn to_radix_digits_le(u: &BigUint, radix: u32) -> Vec { debug_assert!(!u.is_zero() && !radix.is_power_of_two()); + #[cfg(feature = "std")] + let radix_log2 = f64::from(radix).log2(); + #[cfg(not(feature = "std"))] + let radix_log2 = ilog2(radix) as f64; + // Estimate how big the result will be, so we can pre-allocate it. - let radix_digits = ((u.bits() as f64) / f64::from(radix).log2()).ceil(); - let mut res = Vec::with_capacity(radix_digits as usize); + let radix_digits = ((u.bits() as f64) / radix_log2).ceil(); + let mut res = Vec::with_capacity(radix_digits.to_usize().unwrap_or(0)); + let mut digits = u.clone(); let (base, power) = get_radix_base(radix, big_digit::HALF_BITS); diff --git a/src/lib.rs b/src/lib.rs index 236ff7a3..351b16cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,9 +103,31 @@ //! The `num-bigint` crate is tested for rustc 1.15 and greater. #![doc(html_root_url = "https://docs.rs/num-bigint/0.2")] -// We don't actually support `no_std` yet, and probably won't until `alloc` is stable. We're just -// reserving this ability with the "std" feature now, and compilation will fail without. -#![cfg_attr(not(feature = "std"), no_std)] +#![no_std] + +#[cfg(feature = "std")] +#[macro_use] +extern crate std; + +#[cfg(feature = "std")] +mod std_alloc { + pub use std::borrow::Cow; + pub use std::boxed::Box; + pub use std::string::String; + pub use std::vec::Vec; +} + +#[cfg(not(feature = "std"))] +#[macro_use] +extern crate alloc; + +#[cfg(not(feature = "std"))] +mod std_alloc { + pub use alloc::borrow::Cow; + pub use alloc::boxed::Box; + pub use alloc::string::String; + pub use alloc::vec::Vec; +} #[cfg(feature = "rand")] extern crate rand; @@ -117,8 +139,9 @@ extern crate num_traits as traits; #[cfg(feature = "quickcheck")] extern crate quickcheck; +use core::fmt; +#[cfg(feature = "std")] use std::error::Error; -use std::fmt; #[macro_use] mod macros; @@ -178,6 +201,7 @@ impl fmt::Display for ParseBigIntError { } } +#[cfg(feature = "std")] impl Error for ParseBigIntError { fn description(&self) -> &str { self.__description() diff --git a/src/monty.rs b/src/monty.rs index 88b417aa..f3dbd781 100644 --- a/src/monty.rs +++ b/src/monty.rs @@ -1,5 +1,6 @@ -use std::mem; -use std::ops::Shl; +use core::mem; +use core::ops::Shl; +use std_alloc::Vec; use traits::{One, Zero}; use big_digit::{self, BigDigit, DoubleBigDigit, SignedDoubleBigDigit}; diff --git a/tests/bigint.rs b/tests/bigint.rs index 911bff00..76fb1588 100644 --- a/tests/bigint.rs +++ b/tests/bigint.rs @@ -20,7 +20,7 @@ use std::{i16, i32, i64, i8, isize}; use std::{u16, u32, u64, u8, usize}; use num_integer::Integer; -use num_traits::{Float, FromPrimitive, Num, One, Pow, Signed, ToPrimitive, Zero}; +use num_traits::{pow, FromPrimitive, Num, One, Pow, Signed, ToPrimitive, Zero}; mod consts; use consts::*; @@ -392,14 +392,14 @@ fn test_convert_f32() { check(&BigInt::zero(), 0.0); check(&BigInt::one(), 1.0); - check(&BigInt::from(u16::MAX), 2.0.powi(16) - 1.0); - check(&BigInt::from(1u64 << 32), 2.0.powi(32)); - check(&BigInt::from_slice(Plus, &[0, 0, 1]), 2.0.powi(64)); + check(&BigInt::from(u16::MAX), pow(2.0_f32, 16) - 1.0); + check(&BigInt::from(1u64 << 32), pow(2.0_f32, 32)); + check(&BigInt::from_slice(Plus, &[0, 0, 1]), pow(2.0_f32, 64)); check( &((BigInt::one() << 100) + (BigInt::one() << 123)), - 2.0.powi(100) + 2.0.powi(123), + pow(2.0_f32, 100) + pow(2.0_f32, 123), ); - check(&(BigInt::one() << 127), 2.0.powi(127)); + check(&(BigInt::one() << 127), pow(2.0_f32, 127)); check(&(BigInt::from((1u64 << 24) - 1) << (128 - 24)), f32::MAX); // keeping all 24 digits with the bits at different offsets to the BigDigits @@ -480,14 +480,14 @@ fn test_convert_f64() { check(&BigInt::zero(), 0.0); check(&BigInt::one(), 1.0); - check(&BigInt::from(u32::MAX), 2.0.powi(32) - 1.0); - check(&BigInt::from(1u64 << 32), 2.0.powi(32)); - check(&BigInt::from_slice(Plus, &[0, 0, 1]), 2.0.powi(64)); + check(&BigInt::from(u32::MAX), pow(2.0_f64, 32) - 1.0); + check(&BigInt::from(1u64 << 32), pow(2.0_f64, 32)); + check(&BigInt::from_slice(Plus, &[0, 0, 1]), pow(2.0_f64, 64)); check( &((BigInt::one() << 100) + (BigInt::one() << 152)), - 2.0.powi(100) + 2.0.powi(152), + pow(2.0_f64, 100) + pow(2.0_f64, 152), ); - check(&(BigInt::one() << 1023), 2.0.powi(1023)); + check(&(BigInt::one() << 1023), pow(2.0_f64, 1023)); check(&(BigInt::from((1u64 << 53) - 1) << (1024 - 53)), f64::MAX); // keeping all 53 digits with the bits at different offsets to the BigDigits diff --git a/tests/biguint.rs b/tests/biguint.rs index 1e23aa17..e417af4f 100644 --- a/tests/biguint.rs +++ b/tests/biguint.rs @@ -19,8 +19,8 @@ use std::{i128, u128}; use std::{u16, u32, u64, u8, usize}; use num_traits::{ - CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Float, FromPrimitive, Num, One, Pow, - ToPrimitive, Zero, + pow, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, FromPrimitive, Num, One, Pow, ToPrimitive, + Zero, }; mod consts; @@ -624,14 +624,14 @@ fn test_convert_f32() { check(&BigUint::zero(), 0.0); check(&BigUint::one(), 1.0); - check(&BigUint::from(u16::MAX), 2.0.powi(16) - 1.0); - check(&BigUint::from(1u64 << 32), 2.0.powi(32)); - check(&BigUint::from_slice(&[0, 0, 1]), 2.0.powi(64)); + check(&BigUint::from(u16::MAX), pow(2.0_f32, 16) - 1.0); + check(&BigUint::from(1u64 << 32), pow(2.0_f32, 32)); + check(&BigUint::from_slice(&[0, 0, 1]), pow(2.0_f32, 64)); check( &((BigUint::one() << 100) + (BigUint::one() << 123)), - 2.0.powi(100) + 2.0.powi(123), + pow(2.0_f32, 100) + pow(2.0_f32, 123), ); - check(&(BigUint::one() << 127), 2.0.powi(127)); + check(&(BigUint::one() << 127), pow(2.0_f32, 127)); check(&(BigUint::from((1u64 << 24) - 1) << (128 - 24)), f32::MAX); // keeping all 24 digits with the bits at different offsets to the BigDigits @@ -701,14 +701,14 @@ fn test_convert_f64() { check(&BigUint::zero(), 0.0); check(&BigUint::one(), 1.0); - check(&BigUint::from(u32::MAX), 2.0.powi(32) - 1.0); - check(&BigUint::from(1u64 << 32), 2.0.powi(32)); - check(&BigUint::from_slice(&[0, 0, 1]), 2.0.powi(64)); + check(&BigUint::from(u32::MAX), pow(2.0_f64, 32) - 1.0); + check(&BigUint::from(1u64 << 32), pow(2.0_f64, 32)); + check(&BigUint::from_slice(&[0, 0, 1]), pow(2.0_f64, 64)); check( &((BigUint::one() << 100) + (BigUint::one() << 152)), - 2.0.powi(100) + 2.0.powi(152), + pow(2.0_f64, 100) + pow(2.0_f64, 152), ); - check(&(BigUint::one() << 1023), 2.0.powi(1023)); + check(&(BigUint::one() << 1023), pow(2.0_f64, 1023)); check(&(BigUint::from((1u64 << 53) - 1) << (1024 - 53)), f64::MAX); // keeping all 53 digits with the bits at different offsets to the BigDigits