From c76d0d3a2bebd8dc02770e8a1eb411d65f8c5a71 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 1 Aug 2019 13:45:39 -0700 Subject: [PATCH 1/3] 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..0a020637 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; +#[cfg(feature = "quickcheck")] +use std_alloc::Box; +use std_alloc::{String, Vec}; #[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..e6e2ae67 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; +#[cfg(feature = "quickcheck")] +use std_alloc::Box; +use std_alloc::{Cow, String, Vec}; #[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 From c1bd573f9a9ecc879154465c4fee13abd1b4093d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 13 Jan 2020 16:53:18 -0800 Subject: [PATCH 2/3] Build serde without default features (no_std) Our `alloc`-based compilation is based on a lack of the `"std"` feature, but `serde` wants an explicit `"alloc"` feature enabled for `Vec`. Instead, we can use it completely `no_std` by manually handling the sequence of data. Our `serde_test`-based testing is moved to a CI crate, so that doesn't "infect" the features of the main crate. --- .travis.yml | 2 +- Cargo.toml | 4 --- ci/big_serde/Cargo.toml | 12 +++++++ tests/serde.rs => ci/big_serde/src/lib.rs | 6 +++- ci/test_full.sh | 6 +++- src/biguint.rs | 43 ++++++++++++----------- 6 files changed, 46 insertions(+), 27 deletions(-) create mode 100644 ci/big_serde/Cargo.toml rename tests/serde.rs => ci/big_serde/src/lib.rs (94%) diff --git a/.travis.yml b/.travis.yml index 606655d6..78d97d46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ matrix: before_script: - rustup target add $TARGET script: - - cargo build --verbose --target $TARGET --no-default-features --features i128 + - cargo build --verbose --target $TARGET --no-default-features --features "i128 serde" - name: "rustfmt" rust: 1.31.0 before_script: diff --git a/Cargo.toml b/Cargo.toml index 08466752..bf0b3b59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,6 @@ features = ["std"] optional = true version = "1.0" default-features = false -features = ["std"] [dependencies.quickcheck] optional = true @@ -64,9 +63,6 @@ optional = true version = "0.8" default-features = false -[dev-dependencies.serde_test] -version = "1.0" - [features] default = ["std"] i128 = ["num-integer/i128", "num-traits/i128"] diff --git a/ci/big_serde/Cargo.toml b/ci/big_serde/Cargo.toml new file mode 100644 index 00000000..2ed7f954 --- /dev/null +++ b/ci/big_serde/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "big_serde" +version = "0.1.0" +authors = ["Josh Stone "] + +[dependencies] +num-traits = "0.2.11" +serde_test = "1.0" + +[dependencies.num-bigint] +features = ["serde"] +path = "../.." diff --git a/tests/serde.rs b/ci/big_serde/src/lib.rs similarity index 94% rename from tests/serde.rs rename to ci/big_serde/src/lib.rs index a0251b9c..4bbb8b52 100644 --- a/tests/serde.rs +++ b/ci/big_serde/src/lib.rs @@ -3,8 +3,12 @@ //! The serialized formats should not change, even if we change our //! internal representation, because we want to preserve forward and //! backward compatibility of serialized data! +//! +//! This test is in a completely separate crate so its `serde_test` +//! dependency does not "infect" the rest of the build with `serde`'s +//! default features, especially not `serde/std`. -#![cfg(feature = "serde")] +#![cfg(test)] extern crate num_bigint; extern crate num_traits; diff --git a/ci/test_full.sh b/ci/test_full.sh index 88bc4e42..c257bd89 100755 --- a/ci/test_full.sh +++ b/ci/test_full.sh @@ -13,7 +13,7 @@ esac case "$TRAVIS_RUST_VERSION" in 1.1[5-9].* | 1.2[0-9].* | 1.3[0-5].*) ;; - *) NO_STD_FEATURES="i128" ;; + *) NO_STD_FEATURES="i128 serde" ;; esac # num-bigint should build and test everywhere. @@ -54,3 +54,7 @@ fi if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then cargo bench --all-features --no-run fi + +case "$STD_FEATURES" in + *serde*) cargo test --manifest-path ci/big_serde/Cargo.toml +esac diff --git a/src/biguint.rs b/src/biguint.rs index e6e2ae67..f6853fd0 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -2678,8 +2678,8 @@ fn u32_from_u128(n: u128) -> (u32, u32, u32, u32) { } #[cfg(feature = "serde")] -#[cfg(not(u64_digit))] impl serde::Serialize for BigUint { + #[cfg(not(u64_digit))] fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -2687,14 +2687,11 @@ impl serde::Serialize for BigUint { // Note: do not change the serialization format, or it may break forward // and backward compatibility of serialized data! If we ever change the // internal representation, we should still serialize in base-`u32`. - let data: &Vec = &self.data; + let data: &[u32] = &self.data; data.serialize(serializer) } -} -#[cfg(feature = "serde")] -#[cfg(u64_digit)] -impl serde::Serialize for BigUint { + #[cfg(u64_digit)] fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -2722,19 +2719,6 @@ impl serde::Serialize for BigUint { } #[cfg(feature = "serde")] -#[cfg(not(u64_digit))] -impl<'de> serde::Deserialize<'de> for BigUint { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let data: Vec = Vec::deserialize(deserializer)?; - Ok(biguint_from_vec(data)) - } -} - -#[cfg(feature = "serde")] -#[cfg(u64_digit)] impl<'de> serde::Deserialize<'de> for BigUint { fn deserialize(deserializer: D) -> Result where @@ -2751,6 +2735,22 @@ impl<'de> serde::Deserialize<'de> for BigUint { formatter.write_str("a sequence of unsigned 32-bit numbers") } + #[cfg(not(u64_digit))] + fn visit_seq(self, mut seq: S) -> Result + where + S: SeqAccess<'de>, + { + let len = seq.size_hint().unwrap_or(0); + let mut data = Vec::with_capacity(len); + + while let Some(value) = seq.next_element::()? { + data.push(value); + } + + Ok(biguint_from_vec(data)) + } + + #[cfg(u64_digit)] fn visit_seq(self, mut seq: S) -> Result where S: SeqAccess<'de>, @@ -2763,8 +2763,11 @@ impl<'de> serde::Deserialize<'de> for BigUint { let mut value = BigDigit::from(lo); if let Some(hi) = seq.next_element::()? { value |= BigDigit::from(hi) << 32; + data.push(value); + } else { + data.push(value); + break; } - data.push(value); } Ok(biguint_from_vec(data)) From 6f4c8d5852895630bc352315ecc50fc438c0a7c0 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 13 Jan 2020 18:17:06 -0800 Subject: [PATCH 3/3] Update docs for no_std --- README.md | 8 ++++---- src/lib.rs | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4691c9a7..22633d63 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,10 @@ extern crate num_bigint; ## Features -The `std` crate feature is mandatory and enabled by default. If you depend on -`num-bigint` with `default-features = false`, you must manually enable the -`std` feature yourself. In the future, we hope to support `#![no_std]` with -the `alloc` crate when `std` is not enabled. +The `std` crate feature is enabled by default, and is mandatory before Rust +1.36 and the stabilized `alloc` crate. If you depend on `num-bigint` with +`default-features = false`, you must manually enable the `std` feature yourself +if your compiler is not new enough. Implementations for `i128` and `u128` are only available with Rust 1.26 and later. The build script automatically detects this, but you can make it diff --git a/src/lib.rs b/src/lib.rs index 351b16cc..f23ff8dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,11 @@ //! `std` feature yourself. In the future, we hope to support `#![no_std]` with //! the `alloc` crate when `std` is not enabled. //! +//! The `std` crate feature is enabled by default, and is mandatory before Rust +//! 1.36 and the stabilized `alloc` crate. If you depend on `num-bigint` with +//! `default-features = false`, you must manually enable the `std` feature yourself +//! if your compiler is not new enough. +//! //! Implementations for `i128` and `u128` are only available with Rust 1.26 and //! later. The build script automatically detects this, but you can make it //! mandatory by enabling the `i128` crate feature.