Skip to content

Commit

Permalink
feat: no_std support
Browse files Browse the repository at this point in the history
* no_std support

* Use integer log2 when possible to estimate allocations

* Remove std features from a few dependencies

* Use libm for missing funcs in nostd environment

* Use autocfg to detect presence of i128

* Better CI test for nostd environment

* Move benchmark to a separate crate

* Make the tests pass in no_std builds

* Use thumbv7m target for nostd build test

* Add documentation about the no-std compatibility

* Make zeroize and prime features no_std-compatible

* Test each feature in nostd context

* Fix mac test to work in no_std environment

* Avoid using thread_rng in nostd tests

* Fix prime tests in nostd mode

* Fix all warnings and errors

* Only test nostd builds on nightly on travis

* Travis: Only do nostd builds on nightly
  • Loading branch information
roblabla authored and dignifiedquire committed Oct 13, 2019
1 parent 688ad82 commit 874ab0f
Show file tree
Hide file tree
Showing 34 changed files with 240 additions and 121 deletions.
23 changes: 14 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ autobenches = false
[package.metadata.docs.rs]
features = ["std", "serde", "rand", "prime"]

[[bench]]
harness = false
name = "bench_main"
required-features = ["prime"]

[dependencies]

[dependencies.smallvec]
Expand All @@ -43,37 +38,47 @@ default-features = false
optional = true
version = "0.6"
default-features = false
features = ["std"]

[dependencies.zeroize]
version = "0.6"
optional = true
default-features = false
features = ["zeroize_derive"]

[dependencies.serde]
optional = true
version = "1.0"
default-features = false
features = ["std"]

[dependencies.libm]
version = "0.1.4"

[dependencies.lazy_static]
version = "1.2.0"
default-features = false
# no_std feature is an anti-pattern. Why, lazy_static, why?
# See https://github.com/rust-lang-nursery/lazy-static.rs/issues/150
features = ["spin_no_std"]

[dependencies.byteorder]
version = "1.2.7"
default-features = false

[dev-dependencies]
criterion = "0.2"
rand_chacha = "0.1"
rand_xorshift = "0.1"
rand_isaac = "0.1"

[build-dependencies]
autocfg = "0.1.5"

[dev-dependencies.serde_test]
version = "1.0"

[features]
default = ["std", "i128", "u64_digit"]
i128 = ["num-integer/i128", "num-traits/i128"]
std = ["num-integer/std", "num-traits/std", "smallvec/std", "rand/std"]
std = ["num-integer/std", "num-traits/std", "smallvec/std", "rand/std", "serde/std", "zeroize/std"]
u64_digit = []
prime = ["rand"]
nightly = ["zeroize/nightly", "rand/nightly"]
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ extern crate num_bigint_dig as 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` feature is enabled by default and mandatory to compile on older rust
version.

On Rust 1.36 and later, it is possible to use this crate on no_std target. If
you wish to compile for a target that does not have an `std` crate, you should
use `num-bigint` with `default-features = false`. All other sub-features should
be compatible with no_std. Note that in this mode, `num-bigint` still relies on
the alloc crate, so make sure you define a `global_allocator`.

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
Expand Down
19 changes: 19 additions & 0 deletions benchmark_crate/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "num-bigint-dig-benchmark"
version = "0.0.0"
autobenches = false
publish = false

[[bench]]
harness = false
name = "bench_main"

[dependencies]
num-bigint-dig = { path = "../", features = ["prime", "rand"] }
num-integer = "0.1.39"
num-traits = "0.2.4"
rand = "0.6"
rand_chacha = "0.1"

[dev-dependencies]
criterion = "0.2"
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#![cfg(feature = "rand")]

use criterion::Criterion;
use num_bigint::{BigUint, RandBigInt};
use num_traits::Pow;
Expand Down
File renamed without changes.
Empty file added benchmark_crate/src/lib.rs
Empty file.
33 changes: 5 additions & 28 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,12 @@
extern crate autocfg;
use std::env;
use std::io::Write;
use std::process::{Command, Stdio};

fn main() {
if probe("fn main() { 0i128; }") {
let ac = autocfg::new();

if ac.probe_type("i128") {
println!("cargo:rustc-cfg=has_i128");
} else if env::var_os("CARGO_FEATURE_I128").is_some() {
panic!("i128 support was not detected!");
}
}

/// Test if a code snippet can be compiled
fn probe(code: &str) -> bool {
let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
let out_dir = env::var_os("OUT_DIR").expect("environment variable OUT_DIR");

let mut child = Command::new(rustc)
.arg("--out-dir")
.arg(out_dir)
.arg("--emit=obj")
.arg("-")
.stdin(Stdio::piped())
.spawn()
.expect("rustc probe");

child
.stdin
.as_mut()
.expect("rustc stdin")
.write_all(code.as_bytes())
.expect("write rustc stdin");

child.wait().expect("rustc probe").success()
}
}
18 changes: 18 additions & 0 deletions ci/test_full.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,27 @@ cargo test --verbose
cargo build --no-default-features --features="std"
cargo test --no-default-features --features="std"

# It should build in no_std
if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then
rustup target add thumbv7m-none-eabi
cargo build --no-default-features --target=thumbv7m-none-eabi

# It should work in no_std on nightly.
# Note: Doctest might show an error: https://github.com/rust-lang/rust/issues/54010
# The "error" is wrong however, the doctests still run.
cargo test --no-default-features
fi

# Each isolated feature should also work everywhere.
for feature in $FEATURES; do
cargo build --verbose --no-default-features --features="std $feature"
cargo test --verbose --no-default-features --features="std $feature"

# Ensure that feature also works in nostd context on nightly.
if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then
cargo build --verbose --no-default-features --features="$feature"
cargo test --verbose --no-default-features --features="$feature"
fi
done

# test all supported features together
Expand All @@ -28,5 +45,6 @@ cargo test --features="std $FEATURES"

# make sure benchmarks can be built
if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then
cd benchmark_crate
cargo bench --all-features --no-run
fi
11 changes: 10 additions & 1 deletion src/algorithms/bits.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::mem;
use core::mem;

/// Find last set bit
/// fls(0) == 0, fls(u32::MAX) == 32
Expand All @@ -9,3 +9,12 @@ pub fn fls<T: num_traits::PrimInt>(v: T) -> usize {
pub fn ilog2<T: num_traits::PrimInt>(v: T) -> usize {
fls(v) - 1
}

/// Divide two integers, and ceil the result.
pub fn idiv_ceil<T: num_traits::PrimInt>(a: T, b: T) -> T {
if a % b != T::zero() {
a / b + T::one()
} else {
a / b
}
}
2 changes: 1 addition & 1 deletion src/algorithms/cmp.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::cmp::Ordering::{self, Equal, Greater, Less};
use core::cmp::Ordering::{self, Equal, Greater, Less};

use crate::big_digit::BigDigit;

Expand Down
2 changes: 1 addition & 1 deletion src/algorithms/div.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use num_traits::{One, Zero};
use smallvec::SmallVec;
use std::cmp::Ordering;
use core::cmp::Ordering;

use crate::algorithms::{add2, cmp_slice, sub2};
use crate::big_digit::{self, BigDigit, DoubleBigDigit};
Expand Down
24 changes: 12 additions & 12 deletions src/algorithms/gcd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use crate::bigint::{BigInt, ToBigInt};
use crate::biguint::{BigUint, IntDigits};
use integer::Integer;
use num_traits::{One, Signed, Zero};
use std::borrow::Cow;
use std::ops::Neg;
use alloc::borrow::Cow;
use core::ops::Neg;

/// XGCD sets z to the greatest common divisor of a and b and returns z.
/// If extended is true, XGCD returns their value such that z = a*x + b*y.
Expand Down Expand Up @@ -98,8 +98,8 @@ fn lehmer_gcd(

// Ensure that a >= b
if a < b {
std::mem::swap(&mut a, &mut b);
std::mem::swap(&mut ua, &mut ub);
core::mem::swap(&mut a, &mut b);
core::mem::swap(&mut ua, &mut ub);
}

// loop invariant A >= B
Expand Down Expand Up @@ -277,8 +277,8 @@ pub fn extended_gcd(

// Ensure that a >= b
if a < b {
std::mem::swap(&mut a, &mut b);
std::mem::swap(&mut ua, &mut ub);
core::mem::swap(&mut a, &mut b);
core::mem::swap(&mut ua, &mut ub);
}

let mut q: BigInt = 0.into();
Expand Down Expand Up @@ -527,8 +527,8 @@ fn euclid_udpate(
*q = q_new;
*r = r_new;

std::mem::swap(a, b);
std::mem::swap(b, r);
core::mem::swap(a, b);
core::mem::swap(b, r);

if extended {
// ua, ub = ub, ua - q * ub
Expand All @@ -546,7 +546,7 @@ fn euclid_udpate(
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
use core::str::FromStr;

use num_traits::FromPrimitive;

Expand Down Expand Up @@ -574,11 +574,11 @@ mod tests {
while !r.is_zero() {
let quotient = &old_r / &r;
old_r = old_r - &quotient * &r;
std::mem::swap(&mut old_r, &mut r);
core::mem::swap(&mut old_r, &mut r);
old_s = old_s - &quotient * &s;
std::mem::swap(&mut old_s, &mut s);
core::mem::swap(&mut old_s, &mut s);
old_t = old_t - quotient * &t;
std::mem::swap(&mut old_t, &mut t);
core::mem::swap(&mut old_t, &mut t);
}

(old_r, old_s, old_t)
Expand Down
24 changes: 12 additions & 12 deletions src/algorithms/mac.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::cmp;
use std::iter::repeat;
use core::cmp;
use core::iter::repeat;

use crate::algorithms::{adc, add2, sub2, sub_sign};
use crate::big_digit::{BigDigit, DoubleBigDigit, BITS};
Expand Down Expand Up @@ -310,7 +310,7 @@ mod tests {
#[cfg(feature = "u64_digit")]
#[test]
fn test_mac3_regression() {
let b: Vec<BigDigit> = vec![
let b = [
6871754923702299421,
18286959765922425554,
16443042141374662930,
Expand Down Expand Up @@ -657,7 +657,7 @@ mod tests {
17511662813348858473,
12,
];
let c: Vec<BigDigit> = vec![
let c = [
13147625290258353449,
13817956093586917764,
18028234882233861888,
Expand Down Expand Up @@ -1655,18 +1655,18 @@ mod tests {
1305238720762,
];

let mut a1: Vec<BigDigit> = vec![0; 1341];
let mut a2: Vec<BigDigit> = vec![0; 1341];
let mut a3: Vec<BigDigit> = vec![0; 1341];
let mut a1 = &mut [0; 1341];
let mut a2 = &mut [0; 1341];
let mut a3 = &mut [0; 1341];

//print!("{} {}", b.len(), c.len());
long(&mut a1, &b, &c);
karatsuba(&mut a2, &b, &c);
long(a1, &b, &c);
karatsuba(a2, &b, &c);

assert_eq!(a1, a2);
assert_eq!(&a1[..], &a2[..]);

// println!("res: {:?}", &a1);
toom3(&mut a3, &b, &c);
assert_eq!(a1, a3);
toom3(a3, &b, &c);
assert_eq!(&a1[..], &a3[..]);
}
}
2 changes: 1 addition & 1 deletion src/algorithms/mod_inverse.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::borrow::Cow;
use alloc::borrow::Cow;

use num_traits::{One, Signed};

Expand Down
4 changes: 2 additions & 2 deletions src/algorithms/shl.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::borrow::Cow;
use std::iter::repeat;
use alloc::borrow::Cow;
use core::iter::repeat;

use smallvec::SmallVec;

Expand Down
2 changes: 1 addition & 1 deletion src/algorithms/shr.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::borrow::Cow;
use alloc::borrow::Cow;

use num_traits::Zero;
use smallvec::SmallVec;
Expand Down
4 changes: 2 additions & 2 deletions src/algorithms/sub.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::cmp;
use std::cmp::Ordering::*;
use core::cmp;
use core::cmp::Ordering::*;

use num_traits::Zero;
use smallvec::SmallVec;
Expand Down

0 comments on commit 874ab0f

Please sign in to comment.